标题:[实践OK]父进程退出后如何通知子进程退出,主进程退出对子线程的影响?ps -f -C sudo 和 ps -f -C  multepoolser,ps axjf|less再输入左斜杠后输入cron,pstree。 出处:向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除 时间:Mon, 19 Feb 2018 15:13:06 +0000 作者:jackxiang 地址:https://jackxiang.com/post/9643/ 内容: 背景:主进程退出后,子进程没有退出,http://jackxiang.com/post/6937/ 父进程退出后,子进程还在,怎么杀死,暂别说平滑过渡死,先杀死子进程得了,当父进程意外退出时,比如coredump了,根本没机会主动发送信号或pipe等消息的。用了prctl 这个调用,实践来自@http://blog.csdn.net/gavin_new/article/details/73432013: 1)子进程加上在子进程中给父进程设置当父进程退出时子进程收到的信号: /* 子进程中给父进程设置当父进程退出时子进程收到的信号 */ prctl(PR_SET_PDEATHSIG, SIGHUP); 2)主进程退出退知子进程的信号量执行: if (sig == SIGHUP){ printf("child recv SIGHUP..\n");//主进程退出退知子进程 //kill_signal_master(sig); //不要这行也成,&kill_signal_worker部分应该作了处理。 } 3)实践是否真杀死了? #ps -f -C multepoolser UID PID PPID C STIME TTY TIME CMD root 14096 13713 0 15:58 pts/1 00:00:00 [httpmut: master process] master process apache 14097 14096 99 15:58 pts/1 00:00:02 [httpmut: worker process] worker process #kill -TERM 14096 #ps -f -C multepoolser #子进程也被杀死退出了。 UID PID PPID C STIME TTY TIME CMD ========================具体代码========================= httpmut父进程处理kill信号: if (isParent){//httpmut父进程内容 printf("parent process id %d\n",getpid()); /* 处理父进程接收到的kill信号 */ /* 忽略Broken Pipe信号 */ signal(SIGPIPE, SIG_IGN); /* 处理kill信号 */ signal (SIGINT, kill_signal_master); signal (SIGKILL, kill_signal_master); signal (SIGQUIT, kill_signal_master); signal (SIGTERM, kill_signal_master); signal (SIGHUP, kill_signal_master); /* 处理段错误信号 */ signal(SIGSEGV, kill_signal_master); 处理函数: /* 父进程信号处理 */ void kill_signal_master(const int sig) { long unsigned int i; /* 删除PID文件 */ remove(httpmut_settings_pidfile);//删除掉pid文件 /* 给进程组发送SIGTERM信号,结束子进程 */ for (i=0; i 0) { printf("child %d terminated\n", pid); } } if(sig == SIGQUIT){ //g_bRun = false; //停止运行 printf("用户从键盘按quit键,退出。\n"); } if(sig == SIGSEGV){ //g_bRun = false; //停止运行 printf("线程出现段错误,退出。\n"); } if (sig == SIGHUP){ printf("child recv SIGHUP..\n");//主进程退出退知子进程 kill_signal_master(sig); } exit(0); }   Linux 中创建子进程是相当方便的,通过fork调用即可。当子进程退出时,要给父进程发送SIG_CHLD信号,是为了父进程回收子进程的资源,方便管理的目的。 但是当父进程退出后,一般是不会通知子进程的,父进程会将自己的所有子进程过继给init进程。 但是,在实际的项目中,我们有这样的需求: 如果父进程退出后,希望能通知子进程退出。 我们知道,可以利用进程间通信机制,在父进程退出前主动发送信号或pipe或其他手段告知子进程自己退出了。 但是,当父进程意外退出时,比如coredump了,根本没机会主动发送信号或pipe等消息的。 这时怎么办呢? 我们发现 prctl 这个调用, 通过man prctl: PR_SET_PDEATHSIG (since Linux 2.1.57) Set the parent process death signal of the calling process to arg2 (either a signal value in the range 1..maxsig, or 0 to clear). This is the signal that the calling process will get when its parent dies. 据此, 我们可以在子进程中给父进程设置当父进程退出时子进程收到的信号。 prctl(PR_SET_PDEATHSIG, SIGHUP); 代码如下: #include #include #include #include #include static int do_abort = 0; void handle_signal(int signo) { if (signo == SIGHUP) { printf("child recv SIGHUP..\n"); do_abort = 1; } } int main(void) { pid_t pid; pid = fork(); char *p = NULL; if (pid == 0) // child { signal(SIGHUP, handle_signal); prctl(PR_SET_PDEATHSIG, SIGHUP); while(!do_abort) { sleep(1); printf("in child...\n"); } printf("child exit...\n"); return 0; } else // parent { int times = 5; while(times -- > 0) { sleep(1); if (times == 3) { printf("memcpy ...\n"); memcpy(p, "Hello", 5); } printf("in parent.\n"); } printf("parent exit..\n"); return 0; } return 0; } 通过测试,如果去掉 prctl调用,当父进程发生段错误退出后,子进程依然继续运行。 如果去掉signal调用,即子进程不捕获SIG_HUP信号,当父进程退出后依然会退出,只是退出的有些不优雅罢了。 来自:http://blog.csdn.net/gavin_new/article/details/73432013 实践如下: #./prctl in parent. in child... memcpy ... child recv SIGHUP.. in child... child exit... 段错误(吐核) 二、主进程退出对子线程的影响,线程退出是什么样的一个情况呢?源码来自:http://originlee.com/2015/04/08/influence-of-main-threads-exiting-to-child-thread/ cat ./pthreadtest.c #include #include #include #include void* func(void* arg) { while (1) { printf("child loops\n"); } return NULL; } int main(int argc, char* argv[]) { pthread_t main_tid = pthread_self(); pthread_t tid = 0; pthread_create(&tid, NULL, func, &main_tid); sleep(1); printf("main exit\n"); return 0; } gcc -o pthreadtest pthreadtest.c -lpthread -lmemcached #./pthreadtest child loops child loops child loops child loops child loops ... child loops child loops child loops main exit 运行上面的代码,会发现程序在打印一定数量的「child loops」和一句「main exit」之后退出,并且在退出之前的最后一句打印是「main exit」。 按照他们的逻辑,你看,因为主线程在打印完「main exit」后退出了,然后子线程也跟着退出了,所以随后就没有子线程的打印了。 但其实这里是混淆了进程退出和线程退出的概念了。实际的情况是主线程中的main函数执行完ruturn后弹栈,然后调用glibc库函数exit,exit进行相关清理工作后调用_exit系统调用退出该进程。所以,这种情况实际上是因为进程运行完毕退出导致所有的线程也都跟着退出了,并非是因为主线程的退出导致子线程也退出。 Linux线程模型 实际上,posix线程和一般的进程不同,在概念上没有主线程和子线程之分(虽然在实际实现上还是有一些区分),如果仔细观察apue或者unp等书会发现基本看不到「主线程」或者「子线程」等词语,在csapp中甚至都是用「对等线程」一词来描述线程间的关系。 在Linux 2.6以后的posix线程都是由用户态的pthread库来实现的。在使用pthread库以后,在用户视角看来,每一个tast_struct就对应一个线程(tast_struct原本是内核对应一个进程的结构),而一组线程以及他们所共同引用的一组资源就是进程。从Linux 2.6开始,内核有了线程组的概念,tast_struct结构中增加了一个tgid(thread group id)字段。getpid(获取进程号)通过系统调用返回的也是tast_struct中的tgid,所以tgid其实就是进程号。而tast_struct中的线程号pid字段则由系统调用syscall(SYS_gettid)来获取。 当线程收到一个kill致命信号时,内核会将处理动作施加到整个线程组上。为了应付「发送给进程的信号」和「发送给线程的信号」,tast_struct里面维护了两套signal_pending,一套是线程组共用的,一套是线程独有的。通过kill发送的致命信号被放在线程组共享的signal_pending中,可以任意由一个线程来处理。而通过pthread_kill发送的信号被放在线程独有的signal_pending中,只能由本线程来处理。 关于线程与信号,apue有这么几句: 每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为以后,所有的线程都必须共享这个处理行为的改变。这样如果一个线程选择忽略某个信号,而其他的线程可以恢复信号的默认处理行为,或者是为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。 如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程仍然会杀掉整个进程。 例如一个程序a.out创建了一个子线程,假设主线程的线程号为9601,子线程的线程号为9602(它们的tgid都是9601),因为默认没有设置信号处理程序,所以如果运行命令kill 9602的话,是可以把9601和9602这个两个线程一起杀死的。如果不知道Linux线程背后的故事,可能就会觉得遇到灵异事件了。 另外系统调用syscall(SYS_gettid)获取的线程号与pthread_self获取的线程号是不同的,pthread_self获取的线程号仅仅在线程所依赖的进程内部唯一,在pthread_self的man page中有这样一段话: Thread IDs are guaranteed to be unique only within a process. A thread ID may be reused after a terminated thread has been joined, or a detached thread has terminated. 所以在内核中唯一标识线程ID的线程号只能通过系统调用syscall(SYS_gettid)获取。 Generated by Jackxiang's Bo-blog 2.1.1 Release