system函数

system函数用方便在一个进程中执行命令行(一行shell命令)。
用法如下:
#include <stdio.h>
#include <stdlib.h> int main()
{
printf("Hello\n");
system("sleep 5");
return 0;
}

在程序中通过system调用了命令行 sleep 5。(这里知识举一个例子,当然可以执行一个类似“ bash test.sh”之类的脚本

在这个小程序的运行时,可以通过ps -aux 看到新增加了三个进程。
一个是我们程序本身a.out
一个是shell进程:sh -c ***
一个是我们执行的命令行进程:sleep进程


system函数的实现:

《APUE》中给出了system的一中实现:

#include        <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h> int
system(const char *cmdstring) /* with appropriate signal handling */
{
pid_t pid;
int status;
struct sigaction ignore, saveintr, savequit;
sigset_t chldmask, savemask; if (cmdstring == NULL)
return(1); /* always a command processor with UNIX */ ignore.sa_handler = SIG_IGN; /* ignore SIGINT and SIGQUIT */
sigemptyset(&ignore.sa_mask);
ignore.sa_flags = 0;
if (sigaction(SIGINT, &ignore, &saveintr) < 0)
return(-1);
if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
return(-1);
sigemptyset(&chldmask); /* now block SIGCHLD */
sigaddset(&chldmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
return(-1); if ((pid = fork()) < 0) {
status = -1; /* probably out of processes */
} else if (pid == 0) { /* child */
/* restore previous signal actions & reset signal mask */
sigaction(SIGINT, &saveintr, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL); execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); /* exec error */
} else { /* parent */
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR) {
status = -1; /* error other than EINTR from waitpid() */
break;
}
} /* restore previous signal actions & reset signal mask */
if (sigaction(SIGINT, &saveintr, NULL) < 0)
return(-1);
if (sigaction(SIGQUIT, &savequit, NULL) < 0)
return(-1);
if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
return(-1); return(status);
}
很多人对这个实现有不少的疑问。疑问点主要在下面几个方面:
首先:为什么要忽略中断信号SIGINT和停止信号SIGQUIT。
其次:为什么要锁住子进程结束信号SIGCHLD
另外:为什么锁住了子进程结束信号SIGCHLD后,waitpid还能正确返回。


1. 为什么要忽略中断信号SIGINT和停止信号SIGQUIT。
前面已经介绍,./a.out执行时,产生了三个进程:a.out、(shell进程)sh -c、system调用的命令行产生的进程(如sleep 5, 在apue中是ed进程)。
如果不忽略SIGINT和SIGQUIT会出现上面情况呢,《APUE》中通过自己设计的不屏蔽SIGINT和SIGQUIT信号的实现进行了测试,发现SIGINT和SIGQUIT信号会向./a.out产生的三个进程发送,即会发送给a.out、sh -c、ed。(其中sh -c默认忽略此信号),这样会捕获信号的有a.out、ed。这样在实际运行中会出现上面情况呢:在我们要通过Ctl+c关闭ed(这种交互式程序经常会用ctl+c来关闭)时,信号也被发送给了a.out,也就是a.out和ed都被关闭了,这样可能a.out还有其他工作也不能完成了。
出于以上的原因,我们要求system屏蔽SIGINT和SIGQUIT。

2.为什么要锁住子进程结束信号SIGCHLD
首先明白,如果不锁住SIGCHLD,那么在system执行的命令行创建的子进程(如ed)结束时,会向./a.out也发送SIGCHLD信号。这会产生什么问题呢?
加入我们在调用system之前,./a.out创建了一个子进程(子进程A),并且是通过wait函数等待他结束。但是我们的ed进程结束时就像a.out发送了SIGCHLD,导致wait直接返回,由此a.out就无法等到子进程A结束了。

3.为什么锁住了子进程结束信号SIGCHLD后,waitpid还能正确返回。
这一部分我还没找到确切的证据,但描述的也都是一些合理的推断:
我认为wait和waitpid并不是通过捕获SIGCHLD来返回子进程结束状态的。理由如下:
a.Linux系统对SIGCHLD的默认处理方式就是ignore
b.经常看到一些code里,捕获SIGCHLD,然后再SIGCHLD的信号处理函数中调用wait,来使得父进程不阻塞。
c.waitpid中的option选项为WNOHANG时,waitpid可以立即返回。也就是可以循环调用waitpid来判断子进程是否退出。
基于以上三点,基本上可以退出waitpid并不是通过捕获SIGCHLD来判断子进程状态的。而应该是通过阻塞读取内核某个状态值








APUE学习笔记——10.18 system函数 与waitpid的更多相关文章

  1. APUE学习笔记——10.9 信号发送函数kill、 raise、alarm、pause

    转载注明出处:Windeal学习笔记 kil和raise kill()用来向进程或进程组发送信号 raise()用来向自身进程发送信号. #include <signal.h> int k ...

  2. APUE学习笔记——10信号——信号接口函数 signal 和 sigaction

    signal函数     signal函数是早起Unix系统的信号接口,早期系统中提供不可靠的信号机制.在后来的分支中,部分系统使用原来的不可靠机制定义signal函数,如 Solaris 10 .而 ...

  3. APUE学习笔记——10.11~10.13 信号集、信号屏蔽字、未决信号

    如有转载,请注明出处:Windeal专栏 首先简述下几个概念的关系: 我们通过信号集建立信号屏蔽字,使得信号发生阻塞,被阻塞的信号即未决信号. 信号集: 信号集:其实就是一系列的信号.用sigset_ ...

  4. APUE学习笔记——10 信号

    信号的基本概念     信号是软件中断,信号提供了解决异步时间的方法.     每一中信号都有一个名字,信号名以SIG开头. 产生信号的几种方式     很多条件可以产生信号:     终端交互:用户 ...

  5. APUE学习笔记——8.1-8.4 进程基础

    进程ID 1 进程id是唯一的.(不会有进程id一样的两个进程) 2进程id是可复用的,一个进程销毁后,它的id号可以被新的进程使用.但是Unix采用了延迟复用的算法,也就是进程   销毁后它的id不 ...

  6. APUE学习笔记——10.15 sigsetjmp和siglongjmp

    转载自:sigsetjmp使用方法 如侵犯您的权益,请联系:windeal12@qq.com sigsetjmp使用方法 分类: c/c++ linux2012-02-03 12:33 1252人阅读 ...

  7. APUE学习笔记——10.可靠信号与不可靠信号

    首先说明:现在大部分Unix系系统如Linux都已经实现可靠信号. 1~31信号与SIGRTMIN-SIGRTMAX之间并不是可靠信号与不可靠信号的区别,在大多数系统下他们都是可靠信号. 只不过: 1 ...

  8. Hadoop源码学习笔记(2) ——进入main函数打印包信息

    Hadoop源码学习笔记(2) ——进入main函数打印包信息 找到了main函数,也建立了快速启动的方法,然后我们就进去看一看. 进入NameNode和DataNode的主函数后,发现形式差不多: ...

  9. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

随机推荐

  1. clientWidth offsetWidth scrollWidth

    网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...

  2. jdbctemplate中的queryForInt方法

    今天才发现,原来spring 3.2.2之后,jdbctemplate中的queryForInt已经被取消了! 看下代码: 原来是这样写的: String sql = "SELECT cou ...

  3. Commons Configuration之二基本特性和AbstractConfiguration

    Configuration接口定义一大堆方法.一切从头开始实现这些方法非常困难.因为AbstractConfiguration类存在.该类在Commons Configuration中充当大多数Con ...

  4. NOIP2014 T4 子矩阵 dfs+dp

    最近在狂补题啊QAQ... 打算先把NOIP的干掉吧... 点我看题 链接还是放洛谷的了... 题意:给一个n*m的矩阵,在这个矩阵里选 r 行 c 列,然后这 r 行 c 列所相交的格子为新矩阵的, ...

  5. Sqoop-问题

    1.权限问题 // :: INFO mapreduce.Job: Job job_1555064328415_0328 failed with state FAILED due to: Job set ...

  6. linux一键修改mysql密码脚本

    乱七八糟的shell脚本大集合 #!/bin/bash mysql_root_pwd=$( ; echo) mysql_cnf_path=$ export mysql_passwd=$mysql_ro ...

  7. spring @RequestMapping注解技巧

    @RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一.这个注解会将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上. 下面我们看看,@Request ...

  8. 【转】jQuery的attr与prop

    原文:<jQuery的attr与prop> jQuery1.6中新添加了一个prop方法,看起来和用起来都和attr方法一样,这两个方法有什么区别呢?这要从HTMl 的attribute与 ...

  9. SPOJ8222 NSUBSTR - Substrings

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  10. nginx 开启rewrite thinkcmf

    server{ ... 省略 location / { index index.php index.html index.htm; #如果请求既不是一个文件,也不是一个目录,则执行一下重写规则 if ...