异步清理子进程(Cleaning up children processes asynchronously)

编程入门 行业动态 更新时间:2024-10-26 22:18:08
异步清理子进程(Cleaning up children processes asynchronously)

这是< 高级Linux编程 >,第3.4.4章中的示例。 程序fork()和exec()是一个子进程。 我希望父进程能够异步清理子进程(否则子进程将成为一个僵尸进程),而不是等待进程终止。 可以使用信号SIGCHLD完成。 通过设置signal_handler,我们可以在子进程结束时完成清理工作。 代码如下:

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #include <string.h> int spawn(char *program, char **arg_list){ pid_t child_pid; child_pid = fork(); if(child_pid == 0){ // it is the child process execvp(program, arg_list); fprintf(stderr, "A error occured in execvp\n"); return 0; } else{ return child_pid; } } int child_exit_status; void clean_up_child_process (int signal_number){ int status; wait(&status); child_exit_status = status; // restore the exit status in a global variable printf("Cleaning child process is taken care of by SIGCHLD.\n"); }; int main() { /* Handle SIGCHLD by calling clean_up_process; */ struct sigaction sigchld_action; memset(&sigchld_action, 0, sizeof(sigchld_action)); sigchld_action.sa_handler = &clean_up_child_process; sigaction(SIGCHLD, &sigchld_action, NULL); int child_status; char *arg_list[] = { //deprecated conversion from string constant to char* "ls", "-la", ".", NULL }; spawn("ls", arg_list); return 0; }

但是,当我在终端中运行程序时,父进程永远不会结束。 而且它似乎没有执行函数clean_up_child_process(因为它不打印出“清洁子进程由SIGCHLD处理。”)。 这段代码有什么问题?

This is an example from <Advanced Linux Programming>, chapter 3.4.4. The programs fork() and exec() a child process. Instead of waiting for the termination of the process, I want the parent process to clean up the children process (otherwise the children process will become a zombie process) asynchronously. The can be done using the signal SIGCHLD. By setting up the signal_handler we can make the clean-up work done when the child process ends. And the code the following:

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #include <string.h> int spawn(char *program, char **arg_list){ pid_t child_pid; child_pid = fork(); if(child_pid == 0){ // it is the child process execvp(program, arg_list); fprintf(stderr, "A error occured in execvp\n"); return 0; } else{ return child_pid; } } int child_exit_status; void clean_up_child_process (int signal_number){ int status; wait(&status); child_exit_status = status; // restore the exit status in a global variable printf("Cleaning child process is taken care of by SIGCHLD.\n"); }; int main() { /* Handle SIGCHLD by calling clean_up_process; */ struct sigaction sigchld_action; memset(&sigchld_action, 0, sizeof(sigchld_action)); sigchld_action.sa_handler = &clean_up_child_process; sigaction(SIGCHLD, &sigchld_action, NULL); int child_status; char *arg_list[] = { //deprecated conversion from string constant to char* "ls", "-la", ".", NULL }; spawn("ls", arg_list); return 0; }

However, When I run the program in the terminal, the parent process never ends. And it seems that it doesn't execute the function clean_up_child_process (since it doesn't print out "Cleaning child process is taken care of by SIGCHLD."). What's the problem with this snippet of code?

最满意答案

对于GNU / Linux用户

我已经读过这本书了。 虽然这本书谈到这个机制是:

引自本书3.4.4第59页:

更优雅的解决方案是在子进程终止时通知父进程。

但它只是说你可以使用sigaction来处理这种情况。


以下是如何以这种方式处理进程的完整示例。

首先为什么我们使用这种机制? 好吧,因为我们不希望将所有进程同步在一起。

真实的例子 想象一下,你有10个.mp4文件,你想将它们转换为.mp3文件。 好吧,我的初级用户这样做:

ffmpeg -i 01.mp4 01.mp3

并重复此命令10次。 更高的用户这样做:

ls *.mp4 | xargs -I xxx ffmpeg -i xxx xxx.mp3

这一次,这个命令管道每行10个mp4文件,每个文件一个一个地连接到xargs ,然后逐个转换为mp3 。

但我的高级用户这样做:

ls *.mp4 | xargs -I xxx -P 0 ffmpeg -i xxx xxx.mp3

这意味着如果我有10个文件,则创建10个进程并同时运行它们。 而且有很大的不同。 在前两个命令中,我们只有一个进程; 它被创建然后终止然后继续到另一个。 但是在-P 0选项的帮助下,我们同时创建了10个进程,实际上正在运行10个ffmpeg命令。


现在, 异步清理儿童的目的变得更加清洁了。 事实上,我们想要运行一些新流程,但这些流程的顺序以及它们的退出状态对我们来说无关紧要。 通过这种方式,我们可以尽可能快地运行它们并减少时间。


首先,你可以看到man sigaction以获得你想要的更多细节。

第二次看到这个信号号码:

T ❱ kill -l | grep SIGCHLD 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

示例代码

目标:使用SIGCHLD清理子进程

#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <wait.h> #include <unistd.h> sig_atomic_t signal_counter; void signal_handler( int signal_number ) { ++signal_counter; int wait_status; pid_t return_pid = wait( &wait_status ); if( return_pid == -1 ) { perror( "wait()" ); } if( WIFEXITED( wait_status ) ) { printf ( "job [ %d ] | pid: %d | exit status: %d\n",signal_counter, return_pid, WEXITSTATUS( wait_status ) ); } else { printf( "exit abnormally\n" ); } fprintf( stderr, "the signal %d was received\n", signal_number ); } int main() { // now instead of signal function we want to use sigaction struct sigaction siac; // zero it memset( &siac, 0, sizeof( struct sigaction ) ); siac.sa_handler = signal_handler; sigaction( SIGCHLD, &siac, NULL ); pid_t child_pid; ssize_t read_bytes = 0; size_t length = 0; char* line = NULL; char* sleep_argument[ 5 ] = { "3", "4", "5", "7", "9" }; int counter = 0; while( counter <= 5 ) { if( counter == 5 ) { while( counter-- ) { pause(); } break; } child_pid = fork(); // on failure fork() returns -1 if( child_pid == -1 ) { perror( "fork()" ); exit( 1 ); } // for child process fork() returns 0 if( child_pid == 0 ){ execlp( "sleep", "sleep", sleep_argument[ counter ], NULL ); } ++counter; } fprintf( stderr, "signal counter %d\n", signal_counter ); // the main return value return 0; }

这是示例代码的作用:

创建5个子进程 然后进入内部循环并暂停接收信号。 见man pause 然后,当子进程终止时,父进程唤醒并调用signal_handler函数 继续到最后一个: sleep 9

输出:(17表示SIGCHLD )

ALP ❱ ./a.out job [ 1 ] | pid: 14864 | exit status: 0 the signal 17 was received job [ 2 ] | pid: 14865 | exit status: 0 the signal 17 was received job [ 3 ] | pid: 14866 | exit status: 0 the signal 17 was received job [ 4 ] | pid: 14867 | exit status: 0 the signal 17 was received job [ 5 ] | pid: 14868 | exit status: 0 the signal 17 was received signal counter 5

当你运行这个示例代码时,在另一个终端上试试这个:

ALP ❱ ps -o time,pid,ppid,cmd --forest -g $(pgrep -x bash) TIME PID PPID CMD 00:00:00 5204 2738 /bin/bash 00:00:00 2742 2738 /bin/bash 00:00:00 4696 2742 \_ redshift 00:00:00 14863 2742 \_ ./a.out 00:00:00 14864 14863 \_ sleep 3 00:00:00 14865 14863 \_ sleep 4 00:00:00 14866 14863 \_ sleep 5 00:00:00 14867 14863 \_ sleep 7 00:00:00 14868 14863 \_ sleep 9

正如你所看到的, a.out进程有5个孩子。 它们同时运行。 然后, 每当它们终止时, 内核将信号SIGCHLD发送到它们的父节点: a.out

注意

如果我们不使用pause或任何机制以便父级可以wait它的子级,那么我们将放弃创建的进程,并且upstart (=在Ubuntu或init )成为它们的父级。 你可以尝试删除pause()

for GNU/Linux users

I already read this book. Although the book talked about this mechanism as a:

quote from 3.4.4 page 59 of the book:

A more elegant solution is to notify the parent process when a child terminates.

but it just said that you can use sigaction to handle this situation.


Here is a complete example of how to handle processes in this way.

First why do ever we use this mechanism? Well, since we do not want to synchronize all processes together.

real example Imagine that you have 10 .mp4 files and you want to convert them to .mp3 files. Well, I junior user does this:

ffmpeg -i 01.mp4 01.mp3

and repeats this command 10 times. A little higher users does this:

ls *.mp4 | xargs -I xxx ffmpeg -i xxx xxx.mp3

This time, this command pipes all 10 mp4 files per line, each one-by-one to xargs and then they one by one is converted to mp3.

But I senior user does this:

ls *.mp4 | xargs -I xxx -P 0 ffmpeg -i xxx xxx.mp3

and this means if I have 10 files, create 10 processes and run them simultaneously. And there is BIG different. In the two previous command we had only 1 process; it was created then terminated and then continued to another one. But with the help of -P 0 option, we create 10 processes at the same time and in fact 10 ffmpeg commands are running.


Now the purpose of cleaning up children asynchronously becomes cleaner. In fact we want to run some new processes but the order of those process and maybe the exit status of them is not matter for us. In this way we can run them as fast as possible and reduce the time.


First you can see man sigaction for any more details you want.

Second seeing this signal number by:

T ❱ kill -l | grep SIGCHLD 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

sample code

objective: using the SIGCHLD to clean up child process

#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <wait.h> #include <unistd.h> sig_atomic_t signal_counter; void signal_handler( int signal_number ) { ++signal_counter; int wait_status; pid_t return_pid = wait( &wait_status ); if( return_pid == -1 ) { perror( "wait()" ); } if( WIFEXITED( wait_status ) ) { printf ( "job [ %d ] | pid: %d | exit status: %d\n",signal_counter, return_pid, WEXITSTATUS( wait_status ) ); } else { printf( "exit abnormally\n" ); } fprintf( stderr, "the signal %d was received\n", signal_number ); } int main() { // now instead of signal function we want to use sigaction struct sigaction siac; // zero it memset( &siac, 0, sizeof( struct sigaction ) ); siac.sa_handler = signal_handler; sigaction( SIGCHLD, &siac, NULL ); pid_t child_pid; ssize_t read_bytes = 0; size_t length = 0; char* line = NULL; char* sleep_argument[ 5 ] = { "3", "4", "5", "7", "9" }; int counter = 0; while( counter <= 5 ) { if( counter == 5 ) { while( counter-- ) { pause(); } break; } child_pid = fork(); // on failure fork() returns -1 if( child_pid == -1 ) { perror( "fork()" ); exit( 1 ); } // for child process fork() returns 0 if( child_pid == 0 ){ execlp( "sleep", "sleep", sleep_argument[ counter ], NULL ); } ++counter; } fprintf( stderr, "signal counter %d\n", signal_counter ); // the main return value return 0; }

This is what the sample code does:

create 5 child processes then goes to inner-while loop and pauses for receiving a signal. See man pause then when a child terminates, parent process wakes up and calls signal_handler function continue up to the last one: sleep 9

output: (17 means SIGCHLD)

ALP ❱ ./a.out job [ 1 ] | pid: 14864 | exit status: 0 the signal 17 was received job [ 2 ] | pid: 14865 | exit status: 0 the signal 17 was received job [ 3 ] | pid: 14866 | exit status: 0 the signal 17 was received job [ 4 ] | pid: 14867 | exit status: 0 the signal 17 was received job [ 5 ] | pid: 14868 | exit status: 0 the signal 17 was received signal counter 5

when you run this sample code, on the other terminal try this:

ALP ❱ ps -o time,pid,ppid,cmd --forest -g $(pgrep -x bash) TIME PID PPID CMD 00:00:00 5204 2738 /bin/bash 00:00:00 2742 2738 /bin/bash 00:00:00 4696 2742 \_ redshift 00:00:00 14863 2742 \_ ./a.out 00:00:00 14864 14863 \_ sleep 3 00:00:00 14865 14863 \_ sleep 4 00:00:00 14866 14863 \_ sleep 5 00:00:00 14867 14863 \_ sleep 7 00:00:00 14868 14863 \_ sleep 9

As you can see a.out process has 5 children. And They are running simultaneously. Then whenever each of them terminates, kernel sends the signal SIGCHLD to their parent that is: a.out

NOTE

If we do not use pause or any mechanism so that the parent can wait for its children, then we will abandon the created processes and the upstart (= on Ubuntu or init) becomes parent of them. You can try it if you remove pause()

更多推荐

本文发布于:2023-08-07 17:35:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1465501.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:进程   Cleaning   children   processes   asynchronously

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!