子进程中调用system命令执行openssl后,程序退出了,原因是SIGCHLD信号的处理
2014-08-14 08:50
951 查看
源代码:
后来研究了一下,发现是由于父进程中(父进程A),SIGCHLD信号的处理方式被继承到子进程中(子进程B),子进程中调用system,system又会fork一个子进程(子进程C),这样当system的子进程C执行完后,子进程B收到了SIGCHLD信号,于是执行了quit_proc函数一起退出了
由于本身对信号机制不太懂吧,导致这种低级错误,上网搜索了一下,原来是要在子进程中要恢复SIGCHLD信号的默认处理方式就可以了
修改后的源代码:
1、关于在system中获取子进程的返回值与SIGCHLD
在Linux我们一般写的是Server程序,所以,一般在main函数中,首先将进程转换为后台进程,即调用deamon,deamon的一般实现。
deamon的实现中会忽略下面的信号:
signal(SIGINT, SIG_IGN); //当在终端上按下ctrl+c后,会产生SIGINT信号。
signal(SIGHUP, SIG_IGN); //终端退出时,会给所有的进程发送SIGHUP信号。
signal(SIGQUIT, SIG_IGN); //终端退出时,会给所有的进程发送SIGQUIT信号。
signal(SIGPIPE, SIG_IGN); //往没有读进程的管道中进行写操作。
signal(SIGTTOU, SIG_IGN); //后台进程写tty
signal(SIGTTIN, SIG_IGN); //后台进程读tty
signal(SIGCHLD, SIG_IGN);
/*
子进程先于父进程结束时,会给父进程发送SIGCHLD信号
如果
1、父进程没有忽略SGICHLD信号;
或者
2、父进程没有调用wait或waitpid函数。
那么子进程将僵死。
(
在2.6内核,只要父进程显式忽略了SIGCHLD信号,
那么子进程将不会僵死,那么system将得不到子进程的退出状态。
也就是说system函数的返回值并不是子进程退出时的状态。
而2.4内核,只要父进程没有调用wait系列函数,子进程就将僵死。
不论是否忽略了SIGCHLD信号。
)
*/
signal(SIGTERM, SIG_IGN);
/*
当kill pid时,向进程发送SIGTERM信号。
SIGTERM信号的默认处理是进程退出。
SIGTERM是进程在有可能的情况下退出。
注意::
killall -9 process_name
发送的SIGKILL信号,强制进程退出。
*/
如果,我们在我们的server中需要调用system来调用外部脚本或程序来执行某写工作。
例如:
在脚本中通过wget下载文件::
例如::
其中ret用来接收子进程退出是的返回值。即exit的返回值。
但是由于在deamon中忽略了SIGCHLD信号,所以主进程将不再接收子进程的返回值。所以,ret的值不能正确反映子进程的退出状态。
正确的做法是::
signal(SIGCHLD,SIG_DFL); //默认处理方式,是接收子进程的返回值。
system(command);
signal(SIGCHLD,SIG_IGN);
2、system相关问题::
system函数其实是调用fork,exec,waitpid来实现的。
1、fork一个进程;
2、在子进程中调用exec去执行新程序。
3、在父进程中调用waitpid去等待子进程结束。
如果在父进程已经signal(SIGCHLD,SIG_IGN);那么子进程结束时,子进程的返回值不能被waitpid接收。
这个是必须关注的问题。
下面我们来分析system的实现:
下面给出system函数及SIGCHLD信号处理分别在2.6及2.4内核下的区别。system函数源码的一个实现如下:
2.6内核下当父进程未调用wait系列函数等待子进程结束且未显式地忽略SIGCHLD信号,则子进程将成为僵死进程;(如果显示忽略,则子进程不僵死)
而在2.4内核中只要父进程未调用wait系列函数,则子进程就会成为僵死进程,不管是否显式地忽略SIGCHLD信号。
因而在SIGCHLD信号被显式忽略的情况下,2.6内核子进程将直接退出并释放资源,等父进程调用waitpid时,发现对应的pid进程已不存在,因而返回-1错误,errno为10(No child processes);
而在2.4内核下子进程在父进程waitpid之前不会退出,因而waitpid能成功获取子进程状态。
void quit_proc(int t) { kill(0,15); exit(0); } int main_worker(int argc, char *argv[]) { ...... //自定义信号处理函数 signal(SIGCHLD, quit_proc); //子进程结束时, 父进程会收到这个信号然后父进程也退出 for (i=0; i<childcnt; i++) { procid = fork(); if ( !procid ) { //子进程 ...... system("openssl ..."); //程序执行到这里就退出了,下面的跟踪没有打印出来 printf("openssl ok!\n"); } else if ( procid > 0 ) { // } else { i--; sleep( 5 ); } } while (1) sleep(1); return 0; }
后来研究了一下,发现是由于父进程中(父进程A),SIGCHLD信号的处理方式被继承到子进程中(子进程B),子进程中调用system,system又会fork一个子进程(子进程C),这样当system的子进程C执行完后,子进程B收到了SIGCHLD信号,于是执行了quit_proc函数一起退出了
由于本身对信号机制不太懂吧,导致这种低级错误,上网搜索了一下,原来是要在子进程中要恢复SIGCHLD信号的默认处理方式就可以了
修改后的源代码:
void quit_proc(int t) { kill(0,15); exit(0); } int main_worker(int argc, char *argv[]) { ...... //自定义信号处理函数 signal(SIGCHLD, quit_proc); //子进程结束时, 父进程会收到这个信号然后父进程也退出 for (i=0; i<childcnt; i++) { procid = fork(); if ( !procid ) { //子进程 signal(SIGCHLD,SIG_DFL); //子进程以默认的方式处理该信号(system外部命令时不退出) ...... system("openssl ..."); //程序执行到这里就退出了,下面的跟踪没有打印出来 printf("openssl ok!\n"); } else if ( procid > 0 ) { // } else { i--; sleep( 5 ); } } while (1) sleep(1); return 0; }
1、关于在system中获取子进程的返回值与SIGCHLD
在Linux我们一般写的是Server程序,所以,一般在main函数中,首先将进程转换为后台进程,即调用deamon,deamon的一般实现。
deamon的实现中会忽略下面的信号:
signal(SIGINT, SIG_IGN); //当在终端上按下ctrl+c后,会产生SIGINT信号。
signal(SIGHUP, SIG_IGN); //终端退出时,会给所有的进程发送SIGHUP信号。
signal(SIGQUIT, SIG_IGN); //终端退出时,会给所有的进程发送SIGQUIT信号。
signal(SIGPIPE, SIG_IGN); //往没有读进程的管道中进行写操作。
signal(SIGTTOU, SIG_IGN); //后台进程写tty
signal(SIGTTIN, SIG_IGN); //后台进程读tty
signal(SIGCHLD, SIG_IGN);
/*
子进程先于父进程结束时,会给父进程发送SIGCHLD信号
如果
1、父进程没有忽略SGICHLD信号;
或者
2、父进程没有调用wait或waitpid函数。
那么子进程将僵死。
(
在2.6内核,只要父进程显式忽略了SIGCHLD信号,
那么子进程将不会僵死,那么system将得不到子进程的退出状态。
也就是说system函数的返回值并不是子进程退出时的状态。
而2.4内核,只要父进程没有调用wait系列函数,子进程就将僵死。
不论是否忽略了SIGCHLD信号。
)
*/
signal(SIGTERM, SIG_IGN);
/*
当kill pid时,向进程发送SIGTERM信号。
SIGTERM信号的默认处理是进程退出。
SIGTERM是进程在有可能的情况下退出。
注意::
killall -9 process_name
发送的SIGKILL信号,强制进程退出。
*/
如果,我们在我们的server中需要调用system来调用外部脚本或程序来执行某写工作。
例如:
在脚本中通过wget下载文件::
#!/bin/bash FILENAME=$0 PATHNAME=$1 wget $FILENAME $PATHNAME if [ $? -eq 0 ] ; then exit 0 else exit -1 fi
例如::
char command[1024]; url=http://test/test.rar; pathname="./data"; sprintf(command,"./download.sh %s %s",url,pathname); int ret = system(command); if( ret == 0) { //成功 } else { //失败 }注意::
其中ret用来接收子进程退出是的返回值。即exit的返回值。
但是由于在deamon中忽略了SIGCHLD信号,所以主进程将不再接收子进程的返回值。所以,ret的值不能正确反映子进程的退出状态。
正确的做法是::
signal(SIGCHLD,SIG_DFL); //默认处理方式,是接收子进程的返回值。
system(command);
signal(SIGCHLD,SIG_IGN);
2、system相关问题::
system函数其实是调用fork,exec,waitpid来实现的。
1、fork一个进程;
2、在子进程中调用exec去执行新程序。
3、在父进程中调用waitpid去等待子进程结束。
如果在父进程已经signal(SIGCHLD,SIG_IGN);那么子进程结束时,子进程的返回值不能被waitpid接收。
这个是必须关注的问题。
下面我们来分析system的实现:
下面给出system函数及SIGCHLD信号处理分别在2.6及2.4内核下的区别。system函数源码的一个实现如下:
int system(const char * cmdstring) { pid_t pid; int status; if(cmdstring == NULL) { return (1); } if((pid = fork())<0) { status = -1; } else if(pid == 0) { execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); _exit(127); } else { while(waitpid(pid, &status, 0) < 0) { if(errno != EINTR) { status = -1; break; } } } return status; }
2.6内核下当父进程未调用wait系列函数等待子进程结束且未显式地忽略SIGCHLD信号,则子进程将成为僵死进程;(如果显示忽略,则子进程不僵死)
而在2.4内核中只要父进程未调用wait系列函数,则子进程就会成为僵死进程,不管是否显式地忽略SIGCHLD信号。
因而在SIGCHLD信号被显式忽略的情况下,2.6内核子进程将直接退出并释放资源,等父进程调用waitpid时,发现对应的pid进程已不存在,因而返回-1错误,errno为10(No child processes);
而在2.4内核下子进程在父进程waitpid之前不会退出,因而waitpid能成功获取子进程状态。
相关文章推荐
- 10_26 当调用system函数时子进程与父进程对信号的处理,尤其是SIGCHLD
- Windows批处理 调用程序后 不等待子进程 父进程继续执行命令
- Windows批处理 调用程序后 不等待子进程 父进程继续执行命令
- linux下网络程序遭遇SIGPIPE信号进程退出的原因及规避方法
- 调用信号处理程序被捕捉的信号自动地加到进程的当前信号屏蔽字中signal() sigpending()
- linux 程序调用system执行命令
- TCP 回射程序(处理子进程的SIGCHLD信号)
- Python——cmd调用(os.system阻塞处理)(多条命令执行)
- JAVA调用系统命令或可执行程序--返回一个Runtime运行时对象,然后启动另外一个进程来执行命令
- c#关闭系统进程以及如何调用cmd并执行命令
- C# 窗体边角圆弧处理及在C#中调用外部程序(执行批处理,打开窗口等)
- Linux下Java程序调用Openssl命令实现内存中加密数据
- c# 使用Process调用外部程序时等待该进程结束后再执行住进程
- 子进程调用execv函数后,对信号的处理不保留
- system调用与SIGCHLD信号
- c#关闭系统进程以及如何调用cmd并执行命令
- Linux下调用fork或system启动子进程的信号和资源释放相关问题
- 如何在java控制台程序退出时执行特定的处理
- 在程序中调用外部程序,用process返回命令执行结果以及抓取错误信息的方法
- pb中调用API函数后程序莫名关闭退出原因之一