在Linux编程中,我们经常使用 Fork()
。然而不少情况下,fork
是有危险的。但是又不能简单使用vfork
替换就成了。这个笔记说明了两者使用的一些注意点。
本文地址:https://segmentfault.com/a/11…
Reference:
fork与vfork的区别
vfork,fork,exec函数的区别
C语言函数 atexit execl execlp ……(太长了)
vfork() 与 fork()
pid_t vfork(void);
vfork的作用
函数vfork
的作用是创建一个子进程,而这个子进程的作用是用于调用exec()
,从而再执行一个新进程(往往是用来执行其它的程序)
代码空间
在程序的执行效果上,fork
会将父进程的地址空间复制一份,,但是vfork
并不是这么做,而是 vfork 之后的子进程,在调用 exec 或 exit 之前,在父进程的空间中执行。
执行顺序
vfork
保证子进程优先执行到exec
或exit
之前,父进程都不会被调度。fork
父子进程执行顺序不确定。
因此,执行了vfork
之后,子进程请立即执行 exec,而不要再执行一次 fork,否则就可能导致死锁。
或者这么说,如果在exec
或exit
之前依赖于父进程的进一步动作,就会导致死锁
另外请留意,exec
并不是创建进程,只是用新程序替换了当前进程的上下文。
system()的替代
频繁调用system()
之后,有可能卡死不返回,就是因为出现了这个情况。system
是基于fork
实现的,调用后父子进程调用顺序不一定,可能导致system()
调用死锁。
这个危险的函数,现在我们已经禁用了。换成使用vfork
或者是__libc_fork
实现,代码如下(这份是我自己写的,不是公司的代码,不过原理上差不多):
#define _CMD_LEN (256)
static int _system (char *cmd);
int AMCSystemCmd (const char *format, ...)
{
char cmdBuff[_CMD_LEN];
va_list vaList;
va_start (vaList, format);
vsnprintf ((char *)cmdBuff, sizeof(cmdBuff), format, vaList);
va_end (vaList);
return _system ((char *)cmdBuff);
}
extern int __libc_fork (void);
static int _system (char *command)
{
int pid = 0;
int status = 0;
char *argv[4];
extern char **environ;
if (NULL == command) {
return -1;
}
pid = __libc_fork(); /* vfork() also works */
if (pid < 0) {
return -1;
}
if (0 == pid) { /* child process */
_close_all_fds(); /* 这是我自己写的一个函数,用来关闭所有继承的文件描述符。可不用 */
argv[0] = "sh";
argv[1] = "-c";
argv[2] = command;
argv[3] = NULL;
execve ("/bin/sh", argv, environ); /* execve() also an implementation of exec() */
exit (127);
}
// else
/* wait for child process to start */
do
{
if (waitpid (pid, &status, 0) < 0) {
if (errno != EINTR) {
return -1;
}
else {
return status;
}
}
} while (1);
return 0;
}
使用时用AMCSystemCmd()
直接替代system
即可,还支持动态参数列表呢