Linux下调用system()函数导致的问题
原文:http://blog.chinaunix.net/space.php?uid=20732057&do=blog&id=763540
1.执行killall命令杀新启进程时会连原进程一起kill掉.
2.原进程打开的侦听端口,如果新启动的进程不退出无法释放(socket资源未释放).
查看system()代码,原来system函数也是用的fork实现的,示例代码如下:
#include <signal.h>
int system(const char *cmd)
{
int stat;
pid_t pid;
struct sigaction sa, savintr, savequit;
sigset_t saveblock;
if (cmd == NULL)
return(1);
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigemptyset(&savintr.sa_mask);
sigemptyset(&savequit.sa_mask);
sigaction(SIGINT, &sa, &savintr);
sigaction(SIGQUIT, &sa, &savequit);
sigaddset(&sa.sa_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock);
if ((pid = fork()) == 0) {
sigaction(SIGINT, &savintr, (struct sigaction *)0);
sigaction(SIGQUIT, &savequit, (struct sigaction *)0);
sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0);
execl("/bin/sh", "sh", "-c", cmd, (char *)0);
_exit(127);
}
if (pid == -1) {
stat = -1; /* errno comes from fork() */
} else {
while (waitpid(pid, &stat, 0) == -1) {
if (errno != EINTR){
stat = -1;
break;
}
}
}
sigaction(SIGINT, &savintr, (struct sigaction *)0);
sigaction(SIGQUIT, &savequit, (struct sigaction *)0);
sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0);
return(stat);
}
1、关于在system中获取子进程的返回值与SIGCHLD
在Linux我们一般写的是Server程序,所以,一般在main函数中,首先将进程转换为后台进程,即调用deamon,deamon的一般实现,参见::
http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201121611452549/
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);
更多推荐
所有评论(0)