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

来源:互联网 发布:现货数据接口 编辑:程序博客网 时间:2024/05/21 10:40

       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: 10683the count is: 1I am back in the child process, my ID: 10684 and my parent ID: 10683the 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,enterbefore fork,no enter:pid=11384I am back in the parent process, my ID: 11385 and my parent ID: 11384the count is: 1before fork,no enter:pid=11384I am back in the child process, my ID: 11385 and my parent ID: 11384the 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: 12170the count is: 1I am back in the parent process, my ID: 12171 and my parent ID: 12170the count is: 2
从结果中可以知道,vfork 函数创建的子进程是和父进程共享数据的,看count值的输出就知道,并且是先执行子进程,再执行父进程。

参考资料:

《UNIX高级环境编程》

0 0
原创粉丝点击