vfork()
是一个函数原型,用于创建一个新的子进程,与 fork()
类似。它的原型如下:
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
vfork()
函数的返回值在不同情况下有不同的含义:
-
如果
vfork()
返回值是负数,表示创建子进程失败,可能由于系统资源不足或其他错误。此时,可以通过查看errno
变量获取具体的错误代码。 -
如果
vfork()
返回值是0,表示当前代码正在执行的是子进程的上下文中。子进程可以继续执行自己的逻辑,例如调用exec()
启动一个新程序或者使用_exit()
终止自己。 -
如果
vfork()
返回值大于0,表示当前代码正在执行的是父进程的上下文中。返回值是子进程的 PID(进程标识符),可以通过这个 PID 来区分父进程和子进程,并对它们进行不同的操作。
vfork()
函数通过创建一个新的子进程来实现,新的子进程几乎和父进程共享地址空间。与 fork()
不同的是,vfork()
在创建子进程时并不会复制父进程的地址空间,而是直接与父进程共享地址空间。
由于 vfork()
子进程与父进程共享地址空间,所以在子进程执行期间,父进程会被阻塞。子进程在 vfork()
返回后,可以使用 exec
函数族中的一个函数来加载另一个可执行程序,或者调用 _exit()
函数来终止自身。在子进程执行这些操作后,父进程恢复执行。
需要注意的是,由于 vfork()
的特殊性,它的使用方式要比 fork()
更加谨慎。通常情况下,建议使用 fork()
来创建子进程。
vfork()
的主要特点如下:
- 共享地址空间:子进程与父进程共享相同的地址空间,包括代码段、数据段和堆栈等,因此子进程可以直接访问父进程的变量和数据。
- 父进程挂起:在子进程执行期间,父进程会被挂起,直到子进程调用
_exit()
终止自己或者执行了一个新的程序(例如通过exec()
),父进程才会恢复运行。 - 不复制地址空间:与
fork()
不同,vfork()
并不会复制整个地址空间,而是与父进程共享同一份地址空间,因此在子进程中修改变量会影响到父进程。
由于 vfork()
子进程与父进程共享地址空间,因此可以显著减少资源的消耗和复制时间。但需要注意的是,在子进程执行期间,应该避免对共享数据进行修改,以免破坏父进程的状态。
一般情况下,我们通常使用 fork()
来创建新进程,因为它更加安全可靠。只有在确实需要在子进程中立即执行一个新的程序时,才会选择使用 vfork()
。
exit()
是一个函数,用于正常终止一个程序或进程。它的原型如下:
#include <stdlib.h>
void exit(int status);
exit()
函数接受一个整数参数 status
,用于指定程序或进程的退出状态。根据惯例,返回值为 0 表示程序正常终止,非零值表示程序异常终止。
当调用 exit()
函数时,它会执行以下操作:
- 刷新标准 I/O 流(包括 stdout、stderr 等)。
- 调用在使用
atexit()
注册的回调函数(如果有的话),按照注册的相反顺序依次调用这些函数。 - 关闭打开的文件描述符(文件句柄)。
最后,exit()
将传递给它的 status
值返回给调用它的进程或操作系统。这个值可以被父进程通过 wait()
或 waitpid()
等函数获取。
需要注意的是,调用 exit()
函数会立即终止当前进程,不会再执行之后的代码。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int cnt = 0;
pid = vfork(); // 使用 vfork() 创建子进程,子进程与父进程共享地址空间
if (pid > 0) // 父进程逻辑
{
while (1) {
printf("father, pid = %d\n", getpid());
cnt++;
sleep(3);
if (cnt == 6)
break;
}
}
else if (pid == 0) // 子进程逻辑
{
while (1) {
printf("child, pid = %d\n", getpid());
sleep(3);
cnt++;
if (cnt == 3) {
exit(0); // 子进程调用 exit() 终止自己
}
}
}
else // vfork() 失败处理
{
perror("vfork");
return 1;
}
return 0;
}
分析结果可知vfork()
在创建子进程时并不会复制父进程的地址空间,而是直接与父进程共享地址空间。当子进程调用exit(0)退出的时候cnt=3;回到父进程后从3开始继续++;