May 23, 2021 Shell - An example of programming
2. What is a program and what is a process
3. The creation of the process
As a "form" when a program really works, it is necessary to be familiar with some of its related operations, this section mainly describes the concepts and operations related to the process, will introduce the basic concepts including programs, processes, jobs and other basic concepts, as well as process status queries, process communication and other related operations.
A program is a collection of instructions, and a process is the basic unit of program execution.
In order for a program to do its job, it must be run as a process, using processor and
I/O
operations to accomplish a particular task.
In this sense, the program is static, while the process is dynamic.
The process differs from the program in that, in addition to containing instruction data in the program file, the process needs to have a data structure in the kernel that holds the relevant properties of a particular process so that the kernel can better manage and schedule the process, thus completing the task of multi-process collaboration. Therefore, in this sense it can be said that the "above" program, beyond the program instruction itself.
If you develop too many process programs, you may find that a program can create multiple processes and complete tasks through the interaction of multiple processes. U
nder Linux, multi-process creation is usually done
fork
system calls.
In this sense, the program "contains" the process.
Another thing to be clear about is that programs can be described in a variety of different program languages, including C-language programs, assembly language programs, and machine instructions resulting from final compilation.
Here's a quick discussion of how Linux does the process through the shell.
Typically, a process is created after a program file name is typed on the command line. For example
$ sleep 100 &
[1] 9298
pidof
view the process ID of the specified program name:
$ pidof sleep
9298
$ cat /proc/9298/maps
08048000-0804b000 r-xp 00000000 08:01 977399 /bin/sleep
0804b000-0804c000 rw-p 00003000 08:01 977399 /bin/sleep
0804c000-0806d000 rw-p 0804c000 00:00 0 [heap]
b7c8b000-b7cca000 r--p 00000000 08:01 443354
...
bfbd8000-bfbed000 rw-p bfbd8000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
Once the program is executed, it is loaded into memory and becomes a process. The memory image (virtual memory) of the process is shown above, including program instructions, data, and some stack space for program command-line parameters and environment variables, all allocated for dynamic memory requests.
For more information about the procedure of a program executing on the command line, refer to "The moment the program executes under the Linux command line".
In fact, there are other ways to create a process, that is, to have the program run, such as by configuring the system to start automatically when it
man init
or by
crond
at
the program at a time. I
n addition, there is a way to write a shell script, write the program to a script file, when the script file is executed, the program in the file will be executed as a process.
The details of these methods are not covered, and here's how to look at the properties of a process.
It is important to add that executing a program at the command
ulimit
commands, such as the maximum number of file descriptors that a process can open, the maximum stack space, virtual memory space, and so on.
See help
help ulimit
Process-related properties and states can be viewed through the ps command, including information such as the user to which the process
ps
the
cpu
usage of cpu and memory.
Familiarize yourself with how viewing them can help with related statistical analysis and other operations.
View the properties of all current processes in the system:
$ ps -ef
View the process for a program that contains a character in the
ID
is 1.
TTY
for?
The represent is not associated with the terminal:
$ ps -C init
PID TTY TIME CMD
1 ? 00:00:01 init
Select a process started by a specific user:
$ ps -U falcon
Output the specified content in the specified format, the following output command
cpu
usage:
$ ps -e -o "%C %c"
Print
cpu
top 4 programs with the highest cpu usage:
$ ps -e -o "%C %c" | sort -u -k1 -r | head -5
7.5 firefox-bin
1.1 Xorg
0.8 scim-panel-gtk
0.2 scim-bridge
Get the 5 processes that use the largest virtual memory:
$ ps -e -o "%z %c" | sort -n -k1 -r | head -5
349588 firefox-bin
96612 xfce4-terminal
88840 xfdesktop
76332 gedit
58920 scim-panel-gtk
There is a "kinship" relationship between all processes of the system, which can be viewed through
pstree
$ pstree
The system process call tree is printed above, and the call relationship between all active processes in the current system can be seen very clearly.
$ top
The command is characterized by the possible dynamic viewing of process information and, of course, some other
-S
by the size of the cumulative execution time,
-u
by a specified user.
Supplement:
top
supports interactive, such as the u command to display all
u
the user's processes, to kill a process with the
k
command, and to enable batch mode if you use
-n 1
option, as follows:
$ top -n 1 -b
Let's discuss an interesting question: How to make a program run with only one at a time.
This means that when a program is being executed, it can no longer be started. So what do you do?
If the same program is copied into many copies and different file names are placed in different places, this would be bad, so consider the simplest case, which is that the program is unique throughout the system, and the name is unique. In that case, what are the ways to answer the above questions?
The general idea is to check that you have not executed at the beginning of the program, and if so, to stop or to continue with subsequent code.
The strategy is diverse, because the previous assumptions have guaranteed the
ps
corresponding to the program name, one by one compared with their own program name, if already, then they have run.
ps -e -o "%c" | tr -d " " | grep -q ^init$ #查看当前程序是否执行
[ $? -eq 0 ] && exit #如果在,那么退出, $?表示上一条指令是否执行成功
Each run checks for a file that holds
ID
at the specified location, if it does not exist, then continue execution, if it does, then check to see
ID
is running, and if so, exit, otherwise re-write
ID
and continue.
pidfile=/tmp/$0".pid"
if [ -f $pidfile ]; then
OLDPID=$(cat $pidfile)
ps -e -o "%p" | tr -d " " | grep -q "^$OLDPID$"
[ $? -eq 0 ] && exit
fi
echo $$ > $pidfile
#... 代码主体
#设置信号0的动作,当程序退出时触发该信号从而删除掉临时文件
trap "rm $pidfile" 0
More implementation strategy to enjoy themselves!
In addition to ensuring that each process can be carried out smoothly, in order to give priority to certain tasks, the system will use certain scheduling methods when carrying out process scheduling, such as the common scheduling algorithm with priority-based time-chip rotation.
In this case, you
renice
running programs by renice, for example: '
$ ps -e -o "%p %c %n" | grep xfs
5089 xfs 0
$ renice 1 -p 5089
renice: 5089: setpriority: Operation not permitted
$ sudo renice 1 -p 5089 #需要权限才行
[sudo] password for falcon:
5089: old priority 0, new priority 1
$ ps -e -o "%p %c %n" | grep xfs #再看看,优先级已经被调整过来了
5089 xfs 1
Now that you can execute the program from the command line and create a process, there is a way to end it. Y
ou
kill
a process by sending a signal to a process that the user starts himself through the kill command, and of course a
root
can
kill
init
For example
$ sleep 50 & #启动一个进程
[1] 11347
$ kill 11347
kill
command sends a termination signal
SIGTERM
to the program by default, allowing the
kill
can also send other
man 7 signal
or
kill -l
$ man 7 signal
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
For example, send a
SIGSTOP
a program with the
kill
command, pause it, and then send
SIGCONT
signal to keep it running.
$ sleep 50 &
[1] 11441
$ jobs
[1]+ Running sleep 50 &
$ kill -s SIGSTOP 11441 #这个等同于我们对一个前台进程执行CTRL+Z操作
$ jobs
[1]+ Stopped sleep 50
$ kill -s SIGCONT 11441 #这个等同于之前我们使用bg %1操作让一个后台进程运行起来
$ jobs
[1]+ Running sleep 50 &
$ kill %1 #在当前会话(session)下,也可以通过作业号控制进程
$ jobs
[1]+ Terminated sleep 50
The
kill
command provides very good functionality, but it can only control the process based on
ID
or job of the
pkill
killall
that extend the way the process is controlled by the program name or even the user name of the process.
For more usage, please refer to their manuals.
When a program exits, how do you tell if the program exits normally or abnormally? R
emember that classic hello world program
hello world
T
here is always a return
return 0
the code. T
his
return 0
lets the programmer check that the process exits properly.
If the process returns an additional value, it is safe to say that the process exited abnormally because it
return 0
statement.
So how do you check the status of the process exit, which is the returned value?
In
Shell
you can check for this particular
$?
, which holds the exit state after the execution of the last command.
$ test1
bash: test1: command not found
$ echo $?
127
$ cat ./test.c | grep hello
$ echo $?
1
$ cat ./test.c | grep hi
printf("hi, myself!\n");
$ echo $?
0
It appears that returning 0 becomes a subtext, although there is no standard, but 0
$?
the program returns normally, but a non-0 value is always detected when an exception occurs. T
his tells us that at the end of the program it is best to keep
exit 0
so that anyone can detect
$?
If one day someone occasionally uses your program to try to check its exit status,
-1
end of the program, then he will be very distressed and wonder where the program he wrote went wrong, check for half a day without knowing what to do, because he trusted you so much that he didn't doubt that your programming habits might be different from start to finish!
For ease of design and implementation, usually a large task is divided into smaller modules. H
ow do you communicate between different modules to interact with the data and work together when they start and become processes? M
any methods are mentioned in unIX Environment Advanced Programming, such as pipelines (nameless and named pipes),
signal
Message
queues),
mmap/munmap
semaphore
different threads of processes),
Socket
which supports process communication between different machines), and so on.
Here's a look at some of the uses of piping and signaling mechanisms in Shell programming.
Under Linux, you
|
so that you can use it to connect the input of the 1st program and the output of the previous program, so it is visually called a pipeline. I
n the C language, creating a nameless pipe is simple and
pipe
function, an array of
int
two elements can be passed in.
This array actually holds two file descriptors, and after the parent process writes something into the first file descriptor, the child process can read from the first file descriptor.
If you use more command lines, this |
|
be used frequently.
For example, there is a demonstration above that uses the output of the
ps
command as input to the
grep
command:
$ ps -ef | grep init
May feel that this "tube" is so magical that it can really link the input and output of two programs, how in the end they are implemented?
In fact, when you enter such a set of commands, the current shell parses appropriately, associates the output of the previous process to the output file descriptor of the pipeline, and the input of the next process to the input file descriptor of the pipeline, which is achieved by the input-output redirection function
dup
fcntl
A famous pipeline is actually a file (a nameless pipe is also like a file, although related to two file descriptors, but can only be read on the other side of the write), but this file is special, the operation to meet the first-in, first-out, and if you try to read a famous pipeline without content, then it will be blocked, the same if you try to write to a famous pipeline, and there is currently no program to try to read it, will also be blocked. Here's a look at the results.
$ mkfifo fifo_test #通过mkfifo命令创建一个有名管道
$ echo "fewfefe" > fifo_test
#试图往fifo_test文件中写入内容,但是被阻塞,要另开一个终端继续下面的操作
$ cat fifo_test #另开一个终端,记得,另开一个。试图读出fifo_test的内容
fewfefe
The
echo
cat
cat
are two different programs, in which case there is no
cat
between the two processes started through echo and
echo
But they can still communicate through a well-named pipeline.
Such a means of communication is ideal for certain situations: for example, there is an architecture consisting of two applications, one
fifo_test
through a loop to determine what it is going to do next. I
f the pipeline has no content, it will be blocked there without consuming
fifo_test
a dead loop, and the other will continue to write some control information to fifo_test as a control program to tell the previous program what to do. H
ere's a very simple example. Y
ou can design some control codes, and then the control
fifo_test
and then the application performs different actions based on those control codes.
Of course, you
fifo_test
in addition to the control code to the data.
The code for the application
$ cat app.sh
#!/bin/bash
FIFO=fifo_test
while :;
do
CI=`cat $FIFO` #CI --> Control Info
case $CI in
0) echo "The CONTROL number is ZERO, do something ..."
;;
1) echo "The CONTROL number is ONE, do something ..."
;;
*) echo "The CONTROL number not recognized, do something else..."
;;
esac
done
The code that controls the program
$ cat control.sh
#!/bin/bash
FIFO=fifo_test
CI=$1
[ -z "$CI" ] && echo "the control info should not be empty" && exit
echo $CI > $FIFO
One program controls the work of another program through a pipeline
$ chmod +x app.sh control.sh #修改这两个程序的可执行权限,以便用户可以执行它们
$ ./app.sh #在一个终端启动这个应用程序,在通过./control.sh发送控制码以后查看输出
The CONTROL number is ONE, do something ... #发送1以后
The CONTROL number is ZERO, do something ... #发送0以后
The CONTROL number not recognized, do something else... #发送一个未知的控制码以后
$ ./control.sh 1 #在另外一个终端,发送控制信息,控制应用程序的工作
$ ./control.sh 0
$ ./control.sh 4343
Such an application architecture is ideal for local multi-program task design and, if
web cgi
for remote control requirements. T
he only change in the introduction of
web cgi
is to put the control
./control.sh
under the
cgi
of
web
and modify it to conform to the
CGI
specifications, which include the rendering of the
content-tpye: text/html
at the beginning of the file: text/html and a blank line) and the
(web
input parameters
QUERY_STRING
environment variable).
So a very simple
CGI
program can be written like this:
#!/bin/bash
FIFO=./fifo_test
CI=$QUERY_STRING
[ -z "$CI" ] && echo "the control info should not be empty" && exit
echo -e "content-type: text/html\n\n"
echo $CI > $FIFO
When you actually use it,
control.sh
has access to the
fifo_test
pipeline and write permissions to control
app.sh
app.sh:
http://ipaddress\_or\_dns/cgi-bin/control.sh?0
The question
?
Is followed
QUERY_STRING
similar to
$1
Such an application is of practical significance for remote control, especially for embedded systems. D uring last year's summer program, we achieved remote control of the motor in such a way. F irst, a simple application is implemented to control the rotation of the motor, including control of speed, direction, etc. In order to achieve remote control, we have designed some control codes to control the different properties associated with motor rotation.
In the C language, if you want to use a famous pipeline, similar to a shell, you only use
read
write
and
mkfifo
fifo
Signals are software interrupts, Linux users can
kill
to a process through the kill command, or they can send signals through the keyboard, such as
CTRL+C
may trigger
SGIINT
signal,
CTRL+\
may trigger
SGIQUIT
signal, in addition, the kernel in some cases will also send
SGISEGV
of course, the process itself can also send signals to itself through
kill
raise
and other functions.
For the types of signals supported under Linux, you can view the list and
kill -l
man 7 signal
or kill -l.
For some signals, the process will have a default response action, while for some signals, the process may be ignored directly, of course, the user can also set a special processing function for some signals. I
n a shell, you
trap
in response to a signal (a command or a defined function) with the trap command (the shell built-in command), while in the C language you can register the
signal
of a signal through the signal call.
This is just a
trap
the use of the trap command.
$ function signal_handler { echo "hello, world."; } #定义signal_handler函数
$ trap signal_handler SIGINT #执行该命令设定:收到SIGINT信号时打印hello, world
$ hello, world #按下CTRL+C,可以看到屏幕上输出了hello, world字符串
Similarly, if you set the response action of signal 0,
trap
simulate the registration of the
atexit
program termination function in the C language program, i.e. the signal_handler function set by
trap signal_handler SIGQUIT
signal_handler
when the program exits. S
ignal 0 is a special signal that defines signal number 0 as an empty signal in
POSIX.1
which is often used to determine whether a particular process still exists.
The signal is triggered when a program exits.
$ cat sigexit.sh
#!/bin/bash
function signal_handler {
echo "hello, world"
}
trap signal_handler 0
$ chmod +x sigexit.sh
$ ./sigexit.sh #实际Shell编程会用该方式在程序退出时来做一些清理临时文件的收尾工作
hello, world
When we pass multiple commands through | to
|,\>,<, ;, (,)
and so on are combined, this sequence of commands usually starts multiple processes that communicate between them through pipelines, etc. S
ometimes, while performing a task, there are other tasks that need to be processed, so you often add a command at the end of the command
CTRL+Z
pause the previous command. i
n order to do other tasks. A
fter you've finished some other tasks, switch the background tasks to the fore from the front desk with the
fg
command. S
uch a control process is usually referred to as job control, while those command sequences are referred to as jobs that may involve one or more programs, one or more processes.
Here are a few common job control actions.
$ sleep 50 &
[1] 11137
Use shell
fg
to turn job 1 to the fore desk
CTRL+Z
pause the process
$ fg %1
sleep 50
^Z
[1]+ Stopped sleep 50
$ jobs #查看当前作业情况,有一个作业停止
[1]+ Stopped sleep 50
$ sleep 100 & #让另外一个作业在后台运行
[2] 11138
$ jobs #查看当前作业情况,一个正在运行,一个停止
[1]+ Stopped sleep 50
[2]- Running sleep 100 &
$ bg %1
[2]+ sleep 50 &
However, to use job control at the command line, job control support is required for current shells, kernel terminal drivers, and so on.