Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Shell process operations


May 23, 2021 Shell - An example of programming


Table of contents


Objective

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.

What is a program and what is a process

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.

The creation of the process

Typically, a process is created after a program file name is typed on the command line. For example

Example: Let the program run in the background

$ sleep 100 &
[1] 9298

Example: View the process ID

pidof view the process ID of the specified program name:

$ pidof sleep
9298

Example: View the memory image of the process

$ 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

View the properties and status of the process

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.

Example: View process properties with the ps command

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

Example: View process kinship through pstree

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.

Example: View process information dynamically with top

$ 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

Example: Make sure that only one copy of a particular program is running

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!

Prioritize the process

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: '

Example: Get process priority

$ ps -e -o "%p %c %n" | grep xfs
 5089 xfs               0

Example: Prioritize the process

$ 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

End the process

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

Example: End the process

$ 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

Example: Pause a process

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.

Example: View the process exit status

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!

Process communication

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.

Example: Nameless Pipe

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

Example: Named pipe

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

Example: Signal

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

Job and job control

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.

Example: Create a background process to get the job number and process number of the process

$ sleep 50 &
[1] 11137

Example: Bring the job to the fore desk and pause it

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

Example: View the current job situation

$ jobs            #查看当前作业情况,有一个作业停止
[1]+  Stopped                 sleep 50
$ sleep 100 &     #让另外一个作业在后台运行
[2] 11138
$ jobs            #查看当前作业情况,一个正在运行,一个停止
[1]+  Stopped                 sleep 50
[2]-  Running                 sleep 100 &

Example: Start a stopped process and run in the background

$ 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.

Resources

  • UNIX Environment Advanced Programming