【系统】linux之fork()函数讲解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/vict_wang/article/details/88533496

fork入门知识

一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
note:fork只拷贝下一个要执行的代码到新的进程。

调用fork的同时,我的理解是,已经在内存中创建了“副本”进程,同时返回pid,所以在返回值之前,已经是主进程和子进程同时在运行了(如果fork成功的话),这样,在程序的运行过程中,一次fork返回了两次值在父进程中,fork返回新创建子进程的进程ID,在子进程中,fork返回0,这时候就能够同时跑两个进程了。

进程复制图

在这里插入图片描述

fork系统调用说明

通过man手册我们可以轻松知道fork()包含的头文件<sys/types.h>和<unistd.h>,功能就是创建一个子进程.函数原型:pid_t fork(void),pid_t是带一个代表经常号pid的数据结构.如果创建成功一个子进程,对于父进程来说是返回子进程的ID.而对于子进程来说就是返回0.而返回-1代表创建子进程失败.
 在这里插入图片描述

调用示例

#include <sys/types.h>
#include <unistd.h> //fork()头文件
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>

int main(void)
{
    pid_t pid ;
    signal(SIGCHLD,SIG_IGN);//处理僵尸进程,下面有讲解
    printf("before fork pid:%d\n",getpid());
    int abc = 10;      
    pid = fork();    //创建子进程分配资源并复制父进程的数据,返回两个值
    if(pid == -1)   //错误返回
    {
        perror("tile");
        return -1;
    }
    if(pid > 0)     //父进程空间
    {
        abc++;
        printf("parent:pid:%d \n",getpid());
        printf("abc:%d \n",abc);
        sleep(20);
    }
    else if(pid == 0)
    {   //子进程空间:其中abc初值为10,从父进程复制而来
        abc++;
        printf("child:%d,parent: %d\n",getpid(),getppid());
        printf("abc:%d",abc);
    }
    printf("fork after...\n");
}

运行结果:

before fork pid:27319
parent:pid:27319 
abc:11 
before fork pid:27319
child:27320,parent: 27319
abc:11
fork after...

fork()系统调用注意点

通过以上程序我们可以知道:

  1. fork系统调用之后,父进程和子进程交替执行(顺序随机),并且它们处于不同空间中
  2. fork()函数的一次调用返回2次返回,这个有点抽象难理解,此时二个进程处于独立的空间,它们各自执行者自己的东西,不产生冲突,所以返回2次一次pid ==0,一次pid大于0.而至于是先子进程还是父进程先执行,这没有确切的规定,是随机的.
  3. 将fork()返回值大于零设置为父进程
  4. 执行过程在fork()之后,并不是从头开始,因为在fork()之前,父进程已经为子进程搭建好了运行环境了…理解了上面四点,相信我们应该对此系统调用一定有较深的了解.

关于signal(SIGCHLD,SIG_IGN)

signal(参数一,参数二)

参数一:我们要进行处理的信号。系统的信号我们可以再终端键入 kill -l查看(共64个)。其实这些信号时系统定义的宏。
参数二:我们处理的方式(是系统默认还是忽略还是捕获)。可以写一个handdle函数来处理我们捕获的信号

SIGCHLD信号

子进程结束时, 父进程会收到这个信号。
如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。

SIG_ING

忽略的意思

使用signal(SIGCHLD, SIG_IGN)处理僵尸进程

通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心(忽略),由内核回收
如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号该信号是子进程退出的时候向父进程发送的,此时的效果类似于父进程先退出,子进程由init进程接管。

vfork()系统调用

vfork()在某些情况下,我们知道vfork()与fork()执行结果是一样的,
除了子进程会执行一次exec系统调用或者调用_exit(0)退出.
函数原型:pid_t vfork vfork(void)
具体返回值与其中fork()类似. 
这个函数时是在没有实现写时赋值前提下,所以现在我们并不推荐使用vfork().
结论如下

  1. vfork() 子进程与父进程共享数据段
  2. vfork() 中是子进程先执行,父进程后执行.
    通常我们都是与exec函数在一起,在主进程中替换进程印象.
展开阅读全文

没有更多推荐了,返回首页