《unix高级环境编程》进程控制——创建进程

本文详细解析了UNIX系统中用于创建进程的fork和vfork函数的功能、返回值、执行顺序及实例应用,揭示了两者在数据独立性、执行顺序上的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       UNIX 中进程创建有两个函数分别是 fork 和 vfork 函数,下面对这两个函数进行分析。

fork 函数

        在 UNIX 系统中,一个现有进程可以调用 fork 函数创建一个新进程。调用 fork 函数的进程称为父进程,由 fork 创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次,两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。首先看下fork函数的原型;

/* 创建进程 */

/* fork 函数 */
/*
 * 函数功能:创建一个新的进程;
 * 返回值:
 * (1)在父进程中,返回新创建子进程的进程ID;
 * (2)在子进程中,返回0;
 * (3)若出错,则返回-1;
 * 函数原型:
 */
#include <unistd.h>

pid_t fork(void);
        fork 函数调用一次,返回两个值,在父进程中,返回新建子进程的进程ID,因为一个父进程可能有多个子进程,没有获取子进程ID的函数,所以返回子进程的进程ID;在子进程中,返回0,因为子进程的父进程ID可以通过函数 getppid 获取;子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本(UNIX 系统是采用写时复制),意味着父子进程间不共享这些存储空间。UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。fork 之后父进程和子进程的执行顺序是不确定的,根据内核所使用的进程调度算法来执行。


下面先看一个比较简单的例子:

#include "apue.h"
#include <unistd.h>

int main(void)
{
    pid_t pid;
    int count = 0;

    pid = fork();//创建一个子进程;

    if(pid < 0)
    {
        err_sys("Created child process error.\n");
        exit(-1);
    }
    else if(0 == pid)//在子进程中,返回0
    {
        printf("I am back in the child process, my ID: %d and my parent ID: %d\n",getpid(),getppid());
        printf("the count is: %d\n",++count);
    }
    else //在父进程中,返回新建子进程的ID
    {
        printf("I am back in the parent process, my ID: %d and my parent ID: %d\n",pid,getpid());
        printf("the count is: %d\n",++count);
    }
    exit(1);
}
输出结果:

I am back in the parent process, my ID: 10684 and my parent ID: 10683
the count is: 1
I am back in the child process, my ID: 10684 and my parent ID: 10683
the count is: 1
        从输出结果我们可以知道,调用一次fork函数,会返回两次,根据程序的输出,返回之后是先执行父进程,在父进程中,返回值是新建子进程的进程ID,所以此时pid的值即为新建子进程的进程ID,count的值增加1;当父进程结束后,调用子进程,在子进程中返回值是 pid=0,所以想要获取新建子进程的进程ID需要使用 getpid函数(相当于获取当前进程的进程ID),注意:count的值依然为1,因为父进程和子进程的数据空间是独立的,所以父子进程的数据不相互共享,count的初始值都还是0;

接着看第二个例子:

#include <unistd.h>
#include <stdlib.h>
#include "apue.h"
int main(void)
{
    pid_t pid;
    int count = 0;
    printf("before fork,enter\n");

    printf("before fork,no enter:pid=%d",getpid());
    pid = fork();
    if(pid < 0)
    {
        err_sys("Created child process error.\n");
        exit(-1);
    }
    else if(0 == pid)//在子进程中,返回0
    {
        printf("\n");
        printf("I am back in the child process, my ID: %d and my parent ID: %d\n",getpid(),getppid());
        printf("the count is: %d\n",++count);
        printf("\n");
    }
    else //在父进程中,返回新建子进程的ID
    {
        printf("\n");
        printf("I am back in the parent process, my ID: %d and my parent ID: %d\n",pid,getpid());
        printf("the count is: %d\n",++count);
        printf("\n");
    }
    exit(1);
}
输出结果:

before fork,enter
before fork,no enter:pid=11384
I am back in the parent process, my ID: 11385 and my parent ID: 11384
the count is: 1

before fork,no enter:pid=11384
I am back in the child process, my ID: 11385 and my parent ID: 11384
the count is: 1
从结果可以看到,有换行符的 printf 语句输出一次,就是说只在一个进程中输出;

 printf("before fork,enter\n");
没有换行符的 printf 语句输出二次,就是说在每一个进程都输出,这里只有两个进程,所以输出两次,多个进程会输出多次;

 printf("before fork,no enter:pid=%d",getpid());
        出现以上 printf 输出不同的原因很简单,因为 printf 是把数据存储在缓冲区中,在缓冲队列等待输出,若遇到换行符或者刷新缓冲区时,会直接打印到屏幕。所以第一个语句带有换行符时是直接把数据打印到屏幕,不在缓冲队列等待输出,因此,子进程复制父进程的数据段时,stdout输出缓冲区并不存在数据,则只在父进程输出一次;没有换行符的 printf 函数把数据保存在缓冲区队列中,子进程把该数据复制,因此,父、子进程各自输出一次。

vfork 函数

vfork 函数也是在现有的进程上创建子进程,基本操作和fork 函数类似,与 fork 的区别如下:

  1. fork 的子进程的数据是复制父进程的数据,即数据独立;
  2. vfork 的子进程和父进程共享数据;
  3. fork 子进程和父进程的执行顺序根据内核进程调度算法决定;
  4. vfork 的执行顺序是,先执行子进程,再执行父进程;

例如例子1使用 vfork 创建子进程时,输出结果跟上面的不一样;

#include "apue.h"
#include <unistd.h>

int main(void)
{
    pid_t pid;
    int count = 0;

    pid = vfork();//创建一个子进程;

    if(pid < 0)
    {
        err_sys("Created child process error.\n");
        exit(-1);
    }
    else if(0 == pid)//在子进程中,返回0
    {
        printf("I am back in the child process, my ID: %d and my parent ID: %d\n",getpid(),getppid());
        printf("the count is: %d\n",++count);
    }
    else //在父进程中,返回新建子进程的ID
    {
        printf("I am back in the parent process, my ID: %d and my parent ID: %d\n",pid,getpid());
        printf("the count is: %d\n",++count);
    }
    exit(1);
}
输出结果:

I am back in the child process, my ID: 12171 and my parent ID: 12170
the count is: 1
I am back in the parent process, my ID: 12171 and my parent ID: 12170
the count is: 2
从结果中可以知道,vfork 函数创建的子进程是和父进程共享数据的,看count值的输出就知道,并且是先执行子进程,再执行父进程。

参考资料:

《UNIX高级环境编程》

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值