用fork()+exec()运行子进程
诀窍是在子进程中调用exec()
函数,这样原来的父进程就能继续运行了。我们一步一步来看。
- 复制进程
第一步用fork()
系统调用复制当前进程。
进程需要以某种方式区分自己是父进程还是子进程,为此fork()
函数向子进程返回0,向父进程返回非零值。
- 如果是子进程,就调用exec()
这一刻,你有两个完全相同的进程在运行,它们使用相同的代码,但子进程(从fork()
接收到0的那个)现在需要调用exec()
运行程序替换自己:
现在你有两个独立的进程:子进程在运行rssgossip.py脚本,而原来的父进程可以继续做其他事,完全不受干扰。
代码冰箱贴
下面就来修改newshound
程序。代码需要在独立进程中为每条RSS源运行rssgossip.py脚本。我们缩减了代码,你只需关注主循环即可。记得检查错误,千万别把父进程和子进程搞混了!
叉子函数
你可以像这样调用
fork()
:
- pid_t pid = fork();
fork()
会返回一个整型值:为子进程返回0,为父进程返回一个正数。父进程将接收到子进程的进程标识符。什么是
pid_t
?不同操作系统用不同的整数类型保存进程ID,有的用short
,有的用int
,操作系统使用哪种类型,pid_t
就设为哪个。
代码冰箱贴解答
下面就来修改newshound
程序。代码需要在独立进程中为每条RSS源运行rssgossip.py脚本。我们缩减了代码,你只需关注主循环即可。记得检查错误,千万别把父进程和子进程搞混了!
试驾
现在编译并运行代码,将看到:
通过克隆自己,然后在独立进程中运行Python脚本,newshound
成功地为每个RSS源运行了独立的进程,而且最妙的是这些进程将同时运行。
这要比逐条读取新闻源快多了。通过学习用fork()
和exec()
创建并运行独立进程,不但能更好地利用现有软件,而且还能提高程序的性能。
这里没有蠢问题
问:
system()
能在独立进程中运行程序吗?答:可以,但使用
system()
使你没办法控制程序的运行方式。问:克隆进程岂不是很慢?我的意思是在用
exec()
替换子进程前我们还要等fork()
复制完整个进程。答:为了让
fork
进程变快,操作系统使用了很多技巧。比如操作系统不会真的复制父进程的数据,而是让父子进程共享数据。问:这样一来,如果子进程修改了存储器中的数据,岂不是会把事情搞砸?
答:不会,如果操作系统发现子进程要修改存储器,就会为它复制一份。
问:这技术听起来真酷,有名字吗?
答:有,叫“写时复制”(copy-on-write)。
问:
pid_t
就是int
吗?答:这取决于平台,你唯一知道的就是它是整型。
问:我在
int
里保存fork()
调用的结果,程序还是能运行。答:最好还是用
pid_t
来保存进程ID,否则当把代码拿到其他机器上编译时可能会出错。问:为什么Windows不支持
fork()
系统调用?答:Windows管理进程的方式和其他操作系统完全不同,那些用来提高
fork()
效率的方法在Windows上很难实现,这可能就是为什么Windows没有内置的fork()
。问:但Cygwin能让我在Windows中调用
fork()
,对吗?答:是的,为了让Windows的进程看起来和Linux、Unix和Mac的一样,写Cygwin的专家做了很多工作。但由于他们还是需要依靠Windows来创建底层进程,所以Cygwin上的
fork()
要比其他平台上的fork()
慢一些。问:如果我想让代码能在Windows上运行,有其他替代品吗?
答:嗯,有一个名叫
CreateProcess()
的函数,它是一个加强版的system()
。如果你想了解更多信息,可以到http://msdn.microsoft.com搜索CreateProcess。问:多个新闻源的输出为什么不会混在一起?
答:操作系统会确保每个字符串都完整地打印出来。
要点
系统调用是内核中的函数。
exec()
函数比system()
提供了更多控制权。
exec()
函数替换当前进程。
fork()
函数复制当前进程。系统调用在失败时通常返回-1。
系统调用失败以后会把
errno
变量设为错误码。