当前位置:嗨网首页>书籍在线阅读

20-创建并等待新进程

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

5.5.4 创建并等待新进程

ANSI和POSIX都定义了一个用于创建新进程并等待它结束的函数——可以把它想象成是同步创建进程。如果一个进程创建了新进程并立即开始等待它的结束,那就很适合使用下面这个接口:

229.png system()之所以这样命名是因为进程同步创建一般被称为“交付给系统运行”。通常会使用system()来运行简单的工具或shell脚本,大多数期望能够显式获取其返回值。

调用system()会执行参数command所提供的命令,而且还可以为该命令指定参数。“/bin/sh –c”会作为前缀加到command参数前面。通过这种方式,再把整个命令传递给shell。

成功时,返回值是执行command命令得到的返回状态,该状态和执行wait()所获取的状态一致。因此,可以通过WEXITSTATUS获取执行command命令的返回值。如果调用/bin/sh本身失败了,那么从WEXITSTATUS返回的值和调用exit(127)的返回值是一样的。因为也可能是调用的命令返回了127,但没有办法来检测是shell本身发生了错误还是调用command命令执行失败而返回127。失败时,system()调用会返回-1。

如果参数command是NULL且/bin/sh是可用的,system()会返回一个非0值,否则返回0。

在执行command命令过程中,会阻塞SIGCHILD信号,而且SIGINT和SIGQUIT信号会被忽略。忽略SIGINT和SIGQUIT信号很有意义,尤其是在循环内调用system()时。如果在循环内调用system(),那需要保证程序可以正确地检测子进程的退出状态。举个例子:

230.png 利用fork()、exec系统调用和waitpid()实现一个system()是非常有用的练习。你应该自己尝试,因为它融合了本章中的许多概念。为了较完整地介绍这些概念,以下是个示例实现:

231.png

232.png 注意,这个例子没有阻塞或者禁止任何信号,这和正式的system()调用不同。根据程序情况,这可能是好事也可能是坏事。但是至少要保证SIGINT信号不被阻塞,这是很明智的,因为这样可以按照用户的意愿随时终止命令的执行。一个较好的实现可以再添加个指针参数,当指针非空时,表示不同的错误。例如,可能会加入fork_failed 和shell_failed。

**system()调用的安全隐患** system()系统调用存在与execlp()和 execvp()调用(见之前的讨论)相同的安全隐患。永远都不要从设置组ID或设置用户ID的程序中执行system()调用,因为黑客可能会修改环境变量(最常见的是PATH),获得和执行程序一样的权限。前面实现的my_system() 调用,由于使用了shell,也容易受到攻击。 为了避免这些攻击风险,执行设置组ID或设置用户ID的程序应该通过fork()创建进程,通过execl()执行期望的二进制代码,不要使用shell。如果也不用调用外部二进制代码,那会是更佳的解决方案!