精华内容
下载资源
问答
  • vfork
    2022-05-05 15:53:59

    VFORK(2)

    NAME

    vfork - create a child process and block parent(创建子进程并阻塞父进程)

    SYNOPSIS

    函数原型

    #include <sys/types.h>
    #include <unistd.h>
    
    pid_t vfork(void);
    

    glibc的功能测试宏要求(参见feature_test_macros(7)):

    vfork():
        Since glibc 2.12:
            (_XOPEN_SOURCE >= 500) && ! (_POSIX_C_SOURCE >= 200809L)
                || /* Since glibc 2.19: */ _DEFAULT_SOURCE
                || /* Glibc versions <= 2.19: */ _BSD_SOURCE
        Before glibc 2.12:
            _BSD_SOURCE || _XOPEN_SOURCE >= 500
    

    DESCRIPTION

    Standard description

    (From POSIX.1) vfork()函数与fork(2)具有相同的效果,不同的是,如果由vfork()创建的进程修改了除用于存储vfork()返回值的pid_t类型变量之外的任何数据,或者从调用vfork()的函数返回值,则该行为是未定义的。或者在成功调用_exit(2)或exec(3)系列函数之前调用任何其他函数。

    Linux description

    vfork()d与fork(2)一样,创建调用进程的子进程。有关详细信息、返回值和错误,请参阅fork(2)。

    vfork()是clone(2)的特例。它用于创建新进程,而无需复制父进程的页表。在性能敏感的应用程序中,它可能很有用,在这些应用程序中,会创建一个子进程,然后立即发出execve(2)。

    vfork()与fork(2)的不同之处在于,调用线程会被挂起,直到子线程终止(要么是通过调用_exit(2)正常地终止,要么是在传递一个致命信号之后异常地终止),或者调用execve(2)。在此之前,子进程与父进程共享所有内存,包括堆栈。子进程不能从当前函数返回或调用exit(3)(这会调用父进程建立的退出处理程序并刷新父进程的stdio(3)缓冲区),但可以调用_exit(2)

    与fork(2)一样,由vfork()创建的子进程继承调用者进程属性的各种副本(例如,文件描述符、信号处理和当前工作目录);如上所述,vfork()调用的区别仅在于对虚拟地址空间的处理。

    发送给父进程的信号在子进程释放父进程的内存之后到达(即,在子进程终止或调用execve(2)之后)。

    Historic description

    在Linux下,fork(2)是使用copy-on-write页面实现的,所以fork(2)唯一的损失是复制父节点的页表以及为子节点创建唯一的任务结构所需的时间和内存。然而,在过去糟糕的日子里,fork(2)需要对调用者的数据空间进行完整的复制,这通常是不必要的,因为通常在执行exec(3)之后立即执行。因此,为了提高效率,BSD引入了vfork()系统调用,它并不完全复制父进程的地址空间,而是借用父进程的内存和控制线程,直到执行(2)调用或退出发生。当子进程使用父进程的资源时,父进程被挂起。vfork()的使用很棘手:例如,不修改父进程中的数据取决于知道哪些变量保存在寄存器中。

    CONFORMING TO

    4.3BSD; POSIX.1-2001 (但标记为过时). POSIX.1-2008 删除了vfork()的规范。

    标准对vfork()提出的要求要比对fork(2)提出的要求弱,因此,如果两者是同义词,则实现是兼容的。特别是,程序员不能指望父进程保持阻塞,直到子进程终止或调用execve(2),也不能指望任何与共享内存相关的特定行为。

    NOTES

    有些人认为vfork()的语义是一个架构缺陷,bsd手册4.2中说:“当实现了适当的系统共享机制时,这个系统调用将被消除。用户不应该依赖于vfork()的内存共享语义,因为在这种情况下,它将成为fork(2)的同义词。”然而,尽管现代内存管理硬件已经减少了fork(2)和vfork()之间的性能差异,但Linux和其他系统仍然保留vfork()的原因有很多:

    • 一些性能关键型应用程序需要vfork()提供的小性能优势。
    • vfork()可以在缺少内存管理单元(MMU)的系统上实现,但是fork(2)不能在这样的系统上实现。(POSIX.1-2008将vfork()从标准中移除;posix_spawn(3)函数的POSIX原理指出,该函数提供了相当于fork(2)+exec(3)的功能,被设计用于在缺少MMU的系统上实现。)
    • 在内存受限的系统上,vfork()避免了为了执行一个新程序而临时提交内存的需要(参见proc(5)中/proc/sys/vm/overcommit_memory的描述)。(当一个大型父进程希望在子进程中执行一个小助手程序时,这尤其有用。)相比之下,在这个场景中使用fork(2)需要提交与父进程大小相等的内存数量(如果存在严格的过度提交),或者提交过多的内存,并且存在进程被内存不足(OOM)杀手终止的风险。
    Caveats

    子进程应该注意不要以意外的方式修改内存,因为一旦子进程终止或执行另一个程序,父进程就会看到这样的更改。在这方面,信号处理程序可能特别有问题:如果在vfork()的子进程中调用的信号处理程序更改了内存,那么这些更改可能会导致从父进程的角度来看进程状态不一致(例如,内存更改在父进程中是可见的,但打开的文件描述符的状态更改是不可见的)。

    在多线程程序中调用vfork()时,只有调用线程被挂起,直到子进程终止或执行新程序。这意味着子级与其他正在运行的代码共享一个地址空间。如果父进程中的另一个线程更改凭据(使用setuid(2)或类似工具),这可能会很危险,因为现在有两个具有不同权限级别的进程在同一地址空间中运行。作为一个危险的例子,假设一个以root用户身份运行的多线程程序使用vfork()创建了一个子程序。vfork()之后,父进程中的一个线程将该进程丢弃给一个没有特权的用户,以便运行一些不受信任的代码(例如,可能通过使用dlopen(3)打开的插件)。在这种情况下,如果父进程使用mmap(2)映射将由特权子进程执行的代码,则可能会发生攻击。

    Linux notes

    当使用NPTL线程库的多线程程序调用vfork()时,不会调用使用pthread_atfork(3)建立的Fork处理程序。在这种情况下,使用LinuxThreads线程库的程序调用Fork处理程序。(有关Linux线程库的描述,请参阅pthreads(7)。)

    调用vfork()相当于调用clone(2)并指定flags:

    CLONE_VM | CLONE_VFORK | SIGCHLD

    History

    vfork()系统调用出现在3.0BSD中。在4.4BSD中,它被认为是fork(2)的同义词,但是NetBSD再次引入了它;参见⟨http://www.netbsd.org/Documentation/kernel/vfork.html⟩。在Linux中,在2.2.0-pre6之前,它一直相当于fork(2)。从2.2.0-pre9(在i386上,在其他架构上稍晚一些)开始,它是一个独立的系统调用。在glibc 2.0.112中添加了支持。

    BUGS

    信号处理的细节是模糊的,在不同的系统中是不同的。BSD手册中说:“为了避免可能的死锁情况,处于vfork()中间的子进程永远不会发送SIGTTOU或SIGTTIN信号;相反,允许输出或ioctls,输入尝试将导致文件结束指示。”

    SEE ALSO

    clone(2), execve(2), _exit(2), fork(2), unshare(2), wait(2)

    参考文献

    man vfork

    更多相关内容
  • 在linux系统中,fork(),vfork()和clone函数都可以创建一个进程,但是它们的区别是什么呢?本文就这三者做一个较深入的分析,感兴趣的朋友一起看看吧
  • 本文分享了Linux vfork与fork简单对比分析,分享给大家,具体如下: fork相关问题: 一、fork基础了解 fork作用为创建一个子进程,在使用了fork命令后,内核会分配新的内存块和数据结构给子进程,并且将父进程的...
  • vfork和fork_linux_源码

    2021-09-30 01:02:53
    vfork函数示例
  • 主要介绍了深入解读Linux进程函数fork(),vfork(),execX(),分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
  • 主要介绍了简述C语言中system()函数与vfork()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下
  • 进程fork+vfork+clone

    2022-04-15 20:47:20
    fork,vfork,clone都是linux系统调用,这三个函数分别调用sys_fork,sys_vfork,sys_clone,最终都会调用到do_fork函数。差别就在于参数的传递和一些准备工作的不同,上一章节已经详细学习了fork的流程,本章主要...

    fork,vfork,clone都是linux系统调用,这三个函数分别调用sys_fork,sys_vfork,sys_clone,最终都会调用到do_fork函数。差别就在于参数的传递和一些准备工作的不同,上一章节已经详细学习了fork的流程,本章主要专注学习这三个接口函数的使用方法和差异点。

    1 进程的四要素
    linux进程所必须的四个要素:

    程序代码,有一段程序供其执行: 代码不一定是进程专有,可以与其它进程共享
    有自己专用系统堆栈空间:
    有进程控制块(task_struct):
    有独立的存储空间:
    以上4条,缺一不可。如果缺少第四条,那么就称其为"线程"。如果完全没有用户空间,称其位”内核线程“;如果共享用户空间,则称其为”用户线程"。

    2 fork
    系统调用fork,允许父进程创建一个新的进程(子进程)。新的子进程是父进程的翻版:完全继承父进程的栈、数据段、堆和执行文本的拷贝。其接口如下:

    NAME
           fork - create a child process

    SYNOPSIS
           #include <sys/types.h>
           #include <unistd.h>

           pid_t fork(void);
    1
    2
    3
    4
    5
    6
    7
    8
    完成对其调用后将存在两个进程,且每个进程都会从fork的返回处继续执行。这两个进程将执行相同的程序文本段,但却各自拥有不同的栈段、数据段以及堆段拷贝。子进程的栈、数据以及栈段开始时是对父进程内存相应各部分的完全复制。执行fork之后,每个进程均可修改各自的栈数据以及堆中的变量而不影响另一进程。

    在这里插入图片描述
    为调用进程创建一个一模一样的新进程,但父子进程需要改变时候,执行一个copy,但是任何修改都造成分裂,如:chroot, open, 写memory,mmap,sigaction….

     

    fork的示例

    考虑以下代码的输出,假设test.txt中的内容”abcdefghijklmnopqrst…”

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include<fcntl.h>

    int main(void)
    {
            char str[10];
            int count = 1;
            int fd = open("test.txt", O_RDWR);
            if(fork() == 0)
            {
                    int cnt = read(fd, str, 10);
                    printf("Child process : %s\n", (char *)str);
                    printf("This is son, his count is: %d (%p). and his pid is: %d\n", ++count, &count, getpid());
            }
            else
            {
                    int cnt = read(fd, str, 10);
                    printf("Child process : %s\n", (char *)str);
                    printf("This is father, his count is: %d (%p), his pid is: %d\n", count, &count, getpid());
            }

        return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    输出为:

    在这里插入图片描述
    从结果来看,子进程和父进程的PID不同,内存资源count是值的复制,子进程改变了count的值,而父进程中的count的值没有改变,这个过程请参考之前章节的写时复制技术。

     

    两个进程共享了同一个指向文件的结构体,所以当子进程输出“abcdefghij”后,父进程就接着输出"klmnopqrst"

    在这里插入图片描述
    3 vfork
    vfork也是创建子进程,但是子进程共享父进程的空间。在vfork创建子进程之后,父进程阻塞,直到子进程执行exec()或者exit()。vfork设计的最初是因为fork没有实现COW机制,很多情况下fork之后会紧跟着exec,而exec的执行相当于前面fork复制的空间全部变得无用,所以设计了vfork。而现在fork使用了COW,唯一的代价仅仅是复制父进程页表的代价,所以vfork的功能就变得越来越不重要。

     

    NAME
           vfork - create a child process and block parent

    SYNOPSIS
           #include <sys/types.h>
           #include <unistd.h>

           pid_t vfork(void);
    1
    2
    3
    4
    5
    6
    7
    8
    vfork因为如下两个特性而更具效率,也是区别与fork所在:

    无需为子进程复制虚拟内存页或页表,相反,子进程共享父进程的内存,直至其成功执行exec或调用exit退出
    在子进程调用exit或exec之前,将暂停执行父进程,所以在使用vfork时,一般立即在vfork之后调用exec,如果exec调用失败,子进程应调用exit退出。

    在这里插入图片描述

     

    vfork示例

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>


    int main(void)
    {
        int count =1;
        int child;

        printf("Before create son, the father's count is %d\n",count);
        if(!(child = vfork()))
        {
            int i = 0;
            for( i = 0; i< 3; i++)
            {
                count++;
                printf("This is son This i is: %d count: %d\n", i, count);
                if(i == 2)
                {
                    printf("This is son This pid is: %d count: %d\n", getpid(), count);    
                    exit(1);
                }
            }
        }
        else
        {
            printf("This is father This pid is: %d count: %d\n", getpid(), count); 
        }
        return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    输出:

    在这里插入图片描述
    用vfork创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程
    子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。为了避免这些问题,需要确保一旦调用vfork(),子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用exit函数。子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续。
    值得注意的是用vfork创建的子进程必须显示调用exit()来结束,否则子进程将不能结束,父进程就讲一直阻塞,出现异常
    大家可以实际将上述例子中exit(1)这个注释掉后,会出现什么情况。对于Vfork和fork是类似的,除了下面两点:
    1、阻塞父进程
    2、不复制父进程的页表

     

    之所以vfork要阻塞父进程是因为vfork后父子进程使用的是完全相同的mm_struct,也就是由完全相同的虚拟地址空间,包括栈也相同,所以两个进程就不能同时运行,否则栈就会乱掉。所以vfork后,父进程是阻塞的,直到调用了exec系列或者exit后,这个时候,子进程的mm需要释放,不再与父进程公用,这个时候就可以解除父进程的阻塞状态。

    4 clone
    clone是Linux为创建线程设计的,所以可以说clone是fork的升级版本,不仅可以创建进程或线程,还可以指定创建新的命名空间,有选择的继承父进程的内存、甚至可以将创建出来的进程编程父进程的兄弟进程等。

    clone函数功能强大,待有很多参数,提供了一个非诚灵活自由的常见进程的方法,因此它创建进程要比前面两种方法更为复杂。clone可以有选择继承父进程的资源,你可以选择像vfork一样和父进程共享一个虚拟存储空间,也可以不和父进程共享,甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。

    NAME
           clone, __clone2 - create a child process

    SYNOPSIS
           /* Prototype for the glibc wrapper function */

           #define _GNU_SOURCE
           #include <sched.h>

           int clone(int (*fn)(void *), void *child_stack,
                     int flags, void *arg, ...
                     /* pid_t *ptid, void *newtls, pid_t *ctid */ );
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    参数    含义
    fn为函数指针    此指针指向一个函数体,即想要创建进程的静态程序
    child_stack    为给子进程分配系统堆栈的指针
    arg    传给子进程的参数一般为(0)
    flags    要复制资源的标志,描述你需要从父进程继承那些资源(是资源复制还是共享,在这里设置参数)
    下面是flaga可以取得值

    标志    含义
    CLONE_PARENT    建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
    CLONE_FS    子进程与父进程共享相同的文件系统,包括root、当前目录、umask
    CLONE_FILES    子进程与父进程共享相同的文件描述符(file descriptor)表
    CLONE_NEWNS    在新的namespace启动子进程,namespace描述了进程的文件hierarchy
    CLONE_SIGHAND    子进程与父进程共享相同的信号处理(signal handler)表
    CLONE_PTRACE    若父进程被trace,子进程也被trace
    CLONE_VFORK    父进程被挂起,直至子进程释放虚拟内存资源
    CLONE_VM    子进程与父进程运行于相同的内存空间
    CLONE_PID    子进程在创建时PID与父进程一致
    CLONE_THREAD    Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群

    在这里插入图片描述
    #define _GNU_SOURCE
    #include <sys/wait.h>
    #include <sys/utsname.h>
    #include <sched.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sched.h>
    #define FIBER_STACK 8192

     

    int a;
    void *stack;

    int do_something()
    {
        a = 10;
        printf("This is son, the pid is: %d, the a is: %d\n",getpid(), a);
        free(stack);
        exit(1);
    }

    int main(void)
    {
        void *stack;
        a = 1;
        stack = malloc(FIBER_STACK);
        if(!stack)
        {
            printf("The stack failed\n");
            exit(0);
        }

        printf("Create son thread \n");
        clone(&do_something, (char *)stack + FIBER_STACK, CLONE_VM | CLONE_VFORK, 0);
        printf("This is father, the pid is: %d, the a is: %d\n",getpid(), a);
        return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    输出结果:

    在这里插入图片描述

     

    inux创建线程的API,本质上去调 clone。要求把P2的所有资源的指针,都指向P1。线程,也被称为 Light weight process。而Linux在clone线程时也十分灵活,可以选择共享/不共享部分资源。

    在这里插入图片描述

     

    POSIX标准要求,进程里面如果有多个线程,在用户空间 getpid() 看到的都是同一个id,这个id其实是TGID。一个进程里面创建了多个线程,在/proc 下 的是 tgid,/proc/tgid/task/{pidx,y,z}pthread_self() 看到的是用户空间pthread线程库里获得的id 。

    5. 总结
    下面是三个接口的优缺点对比

    类型    优点    缺点
    fork    1. 接口非常简洁
    2. 将进程“创建”和执行(exec)解耦,提高了灵活性
    3. 刻画了进程间的内在关系(进程树、进程组)    1. 完全拷贝,过于粗暴(不如clone)
    2. 性能差,可扩展性差(不如vfork)
    3. 不可组合(如fork()+pthread())
    vfork    1. 类似fork,但让父子进程共享同一地址空间
    2. 连映射关系都不需要拷贝,性能更好    1. 只能用在fork+exec的场景中
    2. 共享地址空间存在安全问题
    clone    1. fork的进阶版本,可以选择地不拷贝内存
    2. 高度可控,可按照需求调整    接口比fork复杂,选择性拷贝容易出错
     

    展开全文
  • } vfork函数: #include #include pid_t vfork(void); 功能: vfork() 函数和 fork() 函数一样都是在已有的进程中创建一个新的进程,但它们创建的子进程是有区别的。 返回值: 成功:子进程中返回 0,父进程中返回...

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

    #include <sys/types.h>
    #include <unistd.h>
    pid_t fork(void);
    
    fork调用的一个奇妙之处就是它仅仅被调用一次
    却能够返回两次,它可能有三种不同的返回值:
    1)在父进程中,fork返回新创建子进程的进程ID;
    2)在子进程中,fork返回03)如果出现错误,fork返回一个负值;
    在fork函数执行完毕后,如果创建新进程成功
    则出现两个进程,一个是子进程,一个是父进程
    在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID
    我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
    
    
    fork出错可能有两种原因:
    1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
    2)系统内存不足,这时errno的值被设置为ENOMEM。
    创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
    

    子父进程执行过程:

    #include<stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main()
    {
            pid_t pid;
            pid_t fpid;
            pid=getpid();
            printf("before fork:pid=%d\n",getpid());
            fpid=fork();
            printf("after fork:pid=%d\n",getpid());
            if(pid==getpid()){
                    printf("This is father printf,pid=%d\n",pid);
            }
            else{
                    printf("This son printf,pid:%d\n",getpid());
            }
            return 0;
    }
    
    运行结果:
    before fork:pid=14396
    after fork:pid1=4396
    This is father printf,pid=14396
    after fork:pid14397
    This son printf,pid:14397
    
    由结果可知在程序中父进程会把符合条件的整个代码
    都执行一遍,然后子进程开始执行fork()函数之后的
    代码,子进程执行过程中不会执行fork()函数之前的
    代码,但是可以访问fork()函数之前父进程中的变量。
    在语句fpid=fork()之前,只有一个进程在执行这段代
    码,但在这条语句之后,就变成两个进程在执行了
    

    fork函数创建的新进程的存储空间是如何分配的?
    每一个进程都有自己的存储空间,创建的新的进程也不例外,在早期linux系统中会把父进程中的命令行参数、堆、栈、未初始化数据、初始化数据和正文全部拷贝一份到自己开辟的内存空间,后来随着linux内核技术的更新,并不是把所有的东西全部拷贝到自己的内存,而是写时拷贝,什么时候会写时才拷贝?很显然,当然是在共享同一块内存的类发生内容改变时,才会发生。

    写时拷贝技术:
    学习过fork我们都知道是父进程创建出一个子进程,子进程作为父进程的副本, 是父进程的拷贝。
    可是每次fork出的子进程难道还要把父进程的各种数据拷贝一份?有人会说不是父子进程不共享各种数据段吗?如全局变量区 ,栈区 , 堆区 。如果不拷贝那不就成共享的吗?其实有关子进程拷贝父进程的数据是这样的。如果子进程只是对父进程的数据进行读取操作,那么子进程用的就是父进程的数据。如果子进程需要对某数据进行修改,那么在修改前,子进程才会拷贝出需要修改的这份数据,对这份备份进行修改。这就满足了父子进程的数据相互独立,互不影响的要求。这么做的初衷也是为了节省内存。

    举个栗子如果一份代码中,定义了10个数据。父进程执行的部分对这10个数据全部进行修改,而子进程执行的部分只修改了一个数据,子进程明明用不到其他9个数据,那还何必让子进程拷贝全部数据,多占用9个永远使用不到的数据内存?
    因此创建子进程只是将原父进程的pcb拷贝了一份。父子进程的pcb全部指向的是父进程原本就有的数据,如果子进程里对数据进行了修改,那么子进程的pcb里指向 被修改的数据的指针会指向一个自己新开辟的内存,新开辟的内存里将父进程的数据拷贝过来,然后再进行修改。这就是写时拷贝技术,顾名思义,只在写的时候才拷贝的技术。

    关于参数的修改问题:

    #include<stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    int main()
    {
            pid_t pid;
            pid_t fpid;
            pid=getpid();
            int data=10;
            printf("before fork:pid=%d\n",getpid());
            fpid=fork();
            printf("after fork:pid%d\n",getpid());
            if(pid==getpid()){
                    printf("This is father printf,pid=%d\n",pid);
            }
            else{
                    printf("This son printf,pid:%d\n",getpid());
                    data=data+10;
            }
            printf("data=%d\n",data);
            return 0;
    }
    运行结果:
    before fork:pid=14462
    after fork:pid14462
    This is father printf,pid=14462
    data=10
    after fork:pid14463
    This son printf,pid:14463
    data=20//当数据发生改变时才会从父进程中将要改变的值拷贝一份到子进程自己开辟的内存中去。
    	  //不影响父进程中的值
    

    fork创建一个子进程的一般目的:

    • .一个父进程希望复制自己,使父子进程同时执行不同的代码段,在这个网络服务进程中是常见的。父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
    • 一个进程要执行一个不同的程序,这对shell常见的情况。在这种情况下,子进程从fork返回后立即调用exec。

    简单使用fork(有bug后续完善):

    #include<stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <unistd.h>
    int main()
    {
            pid_t pid;
            pid_t fpid1,fpid2;
            pid=getpid();
            int data=10;
            while(1){
                    printf("请输入数字:\n");
                    scanf("%d",&data);
                    if(data==1){
                            fpid1=fork();
                            if(fpid1==0){
                                    printf("这是创建的第一个子进程\n");
                                    while(1){
                                            printf("-----------,pid=%d\n",getpid());
                                            sleep(3);
                                    }
                            }
                    }
                    else if(data==2){
                            fpid2=fork();
                            if(fpid2==0){
                                    printf("这是创建的第二个子进程\n");
                                    while(1){
                                            printf("-----------,pid=%d\n",getpid());
                                           sleep(3);
                                    }
                            }
                    }
            }
            return 0;
    }    
    

    vfork函数:

    #include <sys/types.h>
    #include <unistd.h>
    pid_t vfork(void);
    功能:
    	vfork() 函数和 fork() 函数一样都是在已有的进程中创建一个新的进程,但它们创建的子进程是有区别的。
    
    返回值:
        成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为无符号整型。
        失败:返回 -1

    fork() 与 vfock() 都是创建一个进程,那它们有什么区别呢?

    • fork(): 父子进程的执行次序不确定。
      vfork():保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程)之后父进程才可能被调度运行。
    • fork(): 子进程拷贝父进程的地址空间,子进程是父进程的一个复制品。
      vfork():子进程共享父进程的地址空间(准确来说,在调用 exec(进程替换) 或 exit(退出进程) 之前与父进程数据是共享的)

    示例演示:

    #include<stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include<stdlib.h>
    int main()
    {
            pid_t pid;
            pid_t fpid;
            pid=getpid();
            int count=0;
            fpid=vfork();
            if(fpid>0){
                    while(1){
                            printf("这是父进程,PID=%d,count=%d\n",pid,count);
                            sleep(1);
                    }
            }
            else if(fpid==0){
                    while(1){
                            printf("这是子进程,PID=%d\n",getpid());
                            sleep(1);
                            count++;
                            if(count==3){
                                    exit(0);
                            }
                    }
            }
            return 0;
    }
    以下是程序运行的结果:
    这是子进程,PID=17935
    这是子进程,PID=17935
    这是子进程,PID=17935
    这是父进程,PID=17934,count=3
    这是父进程,PID=17934,count=3
    这是父进程,PID=17934,count=3
    由此可看出由vfork创建的子进程在退出前共享父进程地址空间
    因为在子进程退出时父进程没有收集子进程的状态,所以子进程变为僵尸进程。z+表示僵尸进程,s+表示正在运行。
    fhn       17999  0.0  0.0      0     0 pts/2    Z+   21:03   0:00 [vfork] <defunct>
    
    

    进程的退出方式

    (1)正常退出

    • 在main函数中执行return
    • 调用exit()函数
    • 调用_exit()或者_Exit()函数
    • 进程最后一个线程返回
    • 最后一个线程调用pthread_exit

    (2)异常退出

    • 调用about函数
    • 进程受到某个信号(如ctrl+c),而该信号使程序终止

    总结:不管是那种退出方式,最终都会执行内核中的同一段代码。这段代码用来关闭进程中所有打开的文件描述符,释放它所占用的内存和其他资源。

    退出方式比较:

    • exit和return的区别:exit是一个函数,有参数;而return是函数执行完后的返回。exit把控制权交给系统,而return将控制权交给调用函数。
    • exit和abort的区别:exit是正常终止进程,而about是异常终止。
    • exit(int exit_cod):exit中的参数exit_code为0代表进程正常终止,若为其他值表示程序执行过程中有错误发生,比如溢出,除数为0。
    • exit()和_exit()的区别:exit头文件stdlib.h中声明,而_exit()声明在头文件unistd.h中。两个函数均能正常终止进程,但是_exit()会执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。

    父子进程终止的先后顺序不同会产生不同的结果。在子进程退出前父进程退出,则系统会让init进程接管子进程。当子进程先于父进程终止,而父进程又没有调用wait函数等待子进程结束,子进程进入僵死状态,并且会一直保持下去除非系统重启。子进程处于僵死状态是,内核只保存该进程的一些必要信息以备父进程所需。此时子进程始终占用着资源,同时也减少了系统可以创建的最大进程数。如果子进程先于父进程终止,且父进程调用了wait或waitpid函数,则父进程会等待子进程结束。

    等待子进程退出:

    为什么要等待子进程退出?因为创建子进程的目的就是为了执行别的代码,然而子进程代码的执行情况我门不了解,也不知道子进程是不是正常退出,所以我们要等待子进程的退出收集子进程退出时返回的状态(正常退出时:根据退出码查看退出是代码的执行情况,异常退出时:查看异常退出的原因)。如果父进程在子进程退出时没有收集子进程的退出状态,则子进程就会变为僵尸进程(创建子进程后,子进程退出状态不被收集,变成僵尸进程。爹不要它了除非爹死后变孤儿init进程养父接收。如果父进程是死循环,那么该僵尸进程就变成游魂野鬼消耗空间。)。

    wait函数:

    进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *wstatus);
    参数status用来保存被收集进程退出时的一些状态
    它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意
    只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL。
    
    可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:
    1.  WIFEXITED(status) 为非0 → 进程正常结束
    
    	WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
    
     2. WIFSIGNALED(status) 为非0 → 进程异常终止
    
    	WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
    
    3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态
    
    	WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
    
    	WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行
    	
    //下面是使用方法:注意&status是指针
    wpid = wait(&status)
    if(WIFEXITED(status)){	//正常退出
    			printf("I'm parent, The child process "
    					"%d exit normally\n", wpid);
    			printf("return value:%d\n", WEXITSTATUS(status));
     
    		} 
    
    返回值:
    如果成功,wait会返回被收集的子进程的进程ID
    如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
    

    waitpid函数:

    pid_t waitpid(pid_t pid, int *wstatus, int options);
    

    从本质上讲,系统调用waitpid和wait的作用是完全相同的但waitpid多出了两个可由用户控制的参数pid和options。

    • pid:从参数的名字pid和类型pid_t中就可以看出这里需要的是一个进程ID,但当pid取不同的值时,在这里有不同的意义。
    • pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
    • pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
    • pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
    • pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

    options:options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:

    ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

    如果我们不想使用它们,也可以把options设为0,如:ret=waitpid(-1,NULL,0);

    如果使用了WNOHANG(不挂起)参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去,就像当于在父进程执行的闲暇时间检查有没有退出的进程。虽然使用了这个收集到子进程退出的信息,但是子进程还会变为僵尸进程。
    而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。

    waitpid的返回值比wait稍微复杂一些,一共有3种情况:

    • 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
    • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
    • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
    • 当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;
    展开全文
  • fork()和vfork()的学习

    2021-05-10 03:27:32
    2.Vfork和fork的区别: 1> vfork也可创建进程,但它实际上还是调用了fork函数。 2>在说明他们的区别之前我们先看两个程序和执行结果 函数实例1:用fork()函数创建进程 1 #include 2 #include 3 #include 4 #include...

    通过 上一部分的学习,我们了解了进程的概念以及在Linux中进程的实现,此部分我们将具体学习如何在Linux中创建一个进程。

    一前言:

    通过原理知识的学习,我们知道每个进程由进程ID号标识。进程被创建时系统会为其分配一个唯一的进程ID号。当一个进程向其父进程(创建该进程的进程)传递其终止消息时,意味这个进程的整个生命周期结束。此时,该进程占用的所用资源包括进程ID被全部释放。

    那么在Linux中如何创建一个进程呢?

    创建进程有两种方式:一是由操作系统创建,二是由父进程创建的进程(通常为子进程)。

    系统调用fork是创建一个新进程的唯一方式。vfork也可创建进程,但它实际上还是调用了fork函数。

    Tiger-John 说明:

    1.由操作系统创建的进程它们之间是平等的不存在资源继承关系。

    2.由父进程创建的进程通常为子进程它们之间有继承关系。

    3. 在系统启动时,OS会创建一些进程,它们承担着管理和分配系统资源的任务,即系统进程。

    如0号idle进程,它是从无到有诞生的第一个线程,主要用于节能 ;关于 idle进程 , 系统最初引导 0号进程,对应的 PCB为 init_task(),要说明下它是 0号进程 PCB的头,并不是 1号 init进程,在引导结束后即成为 cpu 上的 idle进程。在每个 cpu上都有一个 idle进程,这些进程登记在 init_tasks[]数组中。 idle进程不进入就绪队列,系统稳定后,仅当就绪队列为空的时候 idle 进程才会被调度到,在没有其它进程运行的情况下,它大量时间占用 cpu 。

    1号进程(init进程)它是一个由内核启动的用户级进程 ,它是所有用户进程的父进程。实际上,Linux2.6在初始化阶段首先把它建立为一个内核线程kernel_init:

    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

    参数CLONE_FS | CLONE_FILES | CLONE_SIGHAND表示0号线程和1号线程分别共享文件系统(CLONE_FS)、打开的文件(CLONE_FILES)和信号处理程序(CLONE_SIGHAND)。当调度程序选择到kernel_init内核线程时,kernel_init就开始执行内核的一些初始化函数将系统初始化。

    那么,kernel_init()内核线程是怎样变为用户进程的呢?

    实际上,kernel_init()内核函数中调用了execve()系统调用,该系统调用装入用户态下的可执行程序init(/sbin/init)。注意,内核函数kernel_init()和用户态下的可执行文件init是不同的代码,处于不同的位置,也运行在不同的状态,因此,init是内核线程启动起来的一个普通的进程,这也是用户态下的第一个进程。init进程从不终止,因为它创建和监控操作系统外层所有进程的活动。

    二fork()函数和vfork()函数的学习

    1.fork()函数

    调用fork函数后,当前进程分裂为两个进程,一个是原来的父进程,另一个是刚创建的子进程。父进程调用fork后返回值是子进程的ID,子进程中返回值是0,若进程创建失败,只返回-1。失败原因一般是父进程拥有的子进程个数超过了规定限制(返回EAGAIN)或者内存不足(返回ENOMEM)。我们可以依据返回值判断进程,一般情况下调用fork函数后父子进程谁先执行是未定的,取决于内核所使用的调度算法。一般情况下os让所有进程享有同等执行权,除非某些进程优先级高。若有一个孤儿进程,即父进程先于子进程死去,子进程将会由init进程收养。

    函数实例:通过fork()函数创建一个进程:

    1 #include

    2 #include

    3 #include

    4

    5 main()

    6 {

    7         pid_t pid;

    8         printf("PID before fork() :%d\n",(int)getpid());

    9

    10         pid = fork();

    11         if(pid < 0){

    12                 printf("error in fork!\n");

    13         }

    14         else if(0 == pid){

    15                 printf("I'm the child process, CurpPID is %d,ParentPid is %d    \n",pid,(int)getppid());

    16                 }

    17         else{

    18                 printf("I'm the parent process,child PID is %d,ParentPID is     %d\n",pid,(int)getpid());

    19         }

    20

    程序经过调试后结果如下:

    think@ubuntu:~/work/process_thread/fork$ ./fork

    PID before fork() :4566

    I'm the parent process,child PID is 4567,ParentPID is 4566

    I'm the child process, CurpPID is 0,ParentPid is 4566

    从程序执行结果可以看出: 调后fork()函数后返回两个值,子进程返回值为0,而父进程的返回值为创建的子进程的进程ID。

    Tiger-John说明:

    1> Linux进程一般包括代码段,数据段和堆栈段。代码段存放程序的可执行代码;数据段存放程序的全局变量、常量、静态变量;堆存放动态分配的内存变量,栈用于函数调用,存放函数参数、函数内部定义的局部变量。

    2>有人说调用fork函数后,fork()函数返回了两个值,这是一中错误的说法。其实,当系统调用fork()函数后 fork()会将调用进程的所有内容原封不动的拷贝到新产生的子进程中去, 当前进程分裂成了两个进程分别在执行,互不干扰。

    3>.看一个函数实例来仔细体会一下系统调用fork()函数后是如何执行的。

    1 #include

    2 #include

    3 #include

    4

    5 int main()

    6 {

    7         pid_t pid;

    8         int count = 0;

    9         pid = fork();

    10

    11         printf("This is first time,pid = %d\n",pid);

    12         printf("This is the second time,pid = %d\n",pid);

    13         count++;

    14         printf("count = %d\n",count);

    15

    16         if(pid > 0){

    17                 printf("This is the parent process,the child has the pid :%d    \n",pid);

    18         }

    19         else if(!pid){

    20                 printf("This is the child process.\n");

    21         }

    22         else{

    23                 printf("fork failed.\n");

    24         }

    25

    26         printf("This is third,pid = %d\n",pid);

    27         printf("This is four time,pid = %d\n",pid);

    28         return 0;

    29 }

    程序经过调试后结果

    think@ubuntu:~/work/process_thread/fork1$ ./fork

    This is first time,pid = 4614

    This is the second time,pid = 4614

    count = 1

    This is the parent process,the child has the pid :4614

    This is third,pid = 4614

    This is four time,pid = 4614

    This is first time,pid = 0

    This is the second time,pid = 0

    count = 1

    This is the child process.

    This is third,pid = 0

    This is four time,pid = 0

    think@ubuntu:~/work/process_thread/fork1$

    Tiger-John说明:

    从上面的程序的执行结果我们看到一个奇怪的现象:为什么printf的语句执行两次,

    而那句“count++;”的语句却只执行了一次 ?

    系统在调用fork()后分裂成了两个函数分别执行 ,互不干扰 。

    2.Vfork和fork的区别:

    1> vfork也可创建进程,但它实际上还是调用了fork函数。

    2>在说明他们的区别之前我们先看两个程序和执行结果

    函数实例1:用fork()函数创建进程

    1 #include

    2 #include

    3 #include

    4 #include

    5 int globVar = 5;

    6

    7 int main(void)

    8 {

    9         pid_t pid;

    10         int var = 1;

    11         int i;

    12         printf("fork is different with vfork \n");

    13

    14         pid = fork();

    15         if(!pid){

    16                 i=3;

    17                 while(i-- > 0){

    18                         printf("Child process is running\n");

    19                         globVar++;

    20                         var++;

    21                         sleep(1);

    22                               }

    3                 printf("Child's globVar = %d,var = %d\n",globVar,var);

    24                 }

    25         else if(pid){

    26                 i=5;

    27                 while(i-- > 0){

    28                         printf("Parent process is running\n");

    29                         globVar++;

    30                         var++;

    31                         sleep(1);

    32                                 }

    33                 printf("Parent's globVar = %d,var %d\n",globVar,var);

    34                 exit(0);

    35                 }

    36         else{

    37                 perror("Process creation failed\n");

    38                 exit(-1);

    39             }

    40 }

    程序经过调试后;

    think@ubuntu:~/work/process_thread/fork3$ ./fork

    fork is different with vfork

    Parent process is running

    Child process is running

    Child process is running

    Parent process is running

    Child process is running

    Parent process is running

    Child's globVar = 8,var = 4

    Parent process is running

    Parent process is running

    Parent's globVar = 10,var = 6

    函数实例2:用vfork()函数创建一个进程

    1 #include

    2 #include

    3 #include

    4 #include

    5 int globVar = 5;

    6 int main(void)

    7 {

    8         pid_t pid;

    9         int var = 1;

    10         int i;

    11

    12         printf("fork is different with vfork!\n");

    13

    14         pid = vfork();

    15         if(!pid){

    16                 i=3;

    17                 while(i-- > 0)

    18                 {

    19                         printf("Child process is running\n");

    20                         globVar++;

    21                         var++;

    22                         sleep(1);

    23                 }

    24                 printf("Child's globVar = %d,var =%d\n",globVar,var);

    25         }

    26         else if(pid){

    27                         i = 5;

    28                         while(i-- > 0)

    29                         {

    30                                 printf("Parent process is running\n");

    31                                 globVar++;

    32                                 var++;

    33                                 sleep(1);

    34                         }

    35                 printf("Parent's globVar = %d,var %d\n",globVar,var);

    36                 exit(0);

    37                 }

    38         else {

    39                 perror("Process creation failed\n");

    40                 exit(0);

    41         }

    42

    43 }

    程序经过调试后结果为:

    think@ubuntu:~/work/process_thread/fork3$ ./vfork

    fork is different with vfork!

    Child process is running

    Child process is running

    Child process is running

    Child's globVar = 8,var = 4

    Parent process is running

    Parent process is running

    Parent process is running

    Parent process is running

    Parent process is running

    Parent's globVar = 13,var = 5

    Tiger-John说明:

    我们通过以上两个函数的执行可以看到一些区别:

    1. 使用 fork 创建子进程时,子进程继承了父进程的全局变量和局部变量。在子进程中,最后全局变量 globVar 和局部变量 var 的值均递增 3 ,分别为 8 和 4. 不管是全局变量还是局部变量,子进程与父进程对它们的修改互不影响。

    2. 父进程中两者分别递增 5. 最后结果为 10 和 6

    通过以上程序的运行结果可以证明 fork 的子进程有自己独立的地址空间。

    3. 子进程和父进程的执行顺序是很随意的,没有固定的顺序。父子进程的输出是混杂在一起的。

    --------------------------------------------

    1.用vfork()函数创建子进程后,父进程中globVar和var最后均递增了8.这是因为vfork的子进程共享父进程的地址空间,子进程修改变量对父进程是可见的。

    2.使用vfork()函数子进程后打印的结果是子进程在前,父进程在后,说明vfork()保证子进程先执行,在子进程调用exit获exec之前父进程处于阻塞等待状态。

    3>那么现在来看看fork()和vfork()函数之间的区别:

    (1)fork():使用fork()创建一个子进程时,子进程只是完全复制父进程的资源。这样得到的子进程独立于父进程具有良好的并发性。

    vfork(): 使用 vfor创建一个子进程时,操作系统并不将父进程的地址空间完全复制到子进程。而是子进程共享父进程的地址空间,即子进程完全运行在父进程的地址空间上。子进程对该地址空间中任何数据的修改同样为父进程所见。

    (2)fork():父子进程执行顺序不定;

    vfork():保证子进程先运行,在调用exec或exit之前与父进程共享数据,在它调用exec或exit之后父进程才可能被调度运行。

    (3)vfork保证子进程先运行,在它调用exec或exit后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。0b1331709591d260c1c78e86d0c51c18.png

    展开全文
  • vfork简单介绍

    2015-06-24 20:22:19
    在linux下通过C语言实现了fork与vfork的区别,里面有源码,可以用gcc编译运行分析
  • vfork深度分析看陈皓的VFORK 挂掉的一个问题 vfork创建子进程 #include #include #include #include #include #include #include int main() { pid_t child; if((child = vfork()) == -1) { printf("Fork Error:%s\...
  • linux的fork和vfork

    2022-04-29 17:13:48
    本文简单介绍了linux的fork和vfork的特点和差异,并提供了代码示例演示效果。
  • 辨析fork与vfork

    2022-06-03 15:21:31
    首先,这两个函数都是用于创建一个子进程如果要辨析fork与vfork,一句话通俗的说,fork是复制于父进程、vfork是共享于父进程详细如下:fork函数复制一个父进程的副本,从而拥有独立的代码段,数据段以及堆栈,既称为一...
  • 四)vfork与fork的区别 vfork与fork主要有三点区别: .fork():子进程拷贝父进程的数据段,堆栈段 vfork():子进程与父进程共享数据段 .fork()父子进程的执行次序不确定vfork 保证子进程先运行,在调用 exec 或 exit ...
  • Linux之fork与vfork区别

    2021-05-23 06:06:33
    创建一个新进程的方法只有由某个已存在的进程调用fork()或vfork()1.fork()函数返回值:成功:父进程:返回子进程的PID子进程:返回0失败:父进程返回-1子进程是父进程的一个拷贝。即子进程从父进程得到数据段和堆、...
  • 这里写目录标题fork函数介绍利用fork()创建父子进程vfork函数介绍利用vfork()创建父子进程fork()和vfork()的主要区别 fork函数介绍 在Linux系统内,创建子进程的方法是使用系统调用fork()函数。fork()函数是Linux...
  • vfork()函数详解

    2021-08-30 13:58:21
    vfork vfork的特点 — 创建子进程: ①子进程必定先运行,等到子进程调用exit或者exec后,父进程才能运行 ②父子进程共享空间(共享内存数据) fork 是 创建一个子进程,并把父进程的内存数据copy到子进程中。父子...
  • linux的vfork函数

    2022-05-14 13:52:33
    fork和vfork的区别 首先:vfork会直接使用父进程存储空间,不会进行拷贝 其次:vfork保证子进程先进行运行,当子进程调用exit退出后,父进程才执行 #include <stdio.h> #include <sys/types.h> #...
  • vfork()

    千次阅读 2019-05-17 16:23:29
    vfork():sys_vfork() 经过系统调用sys_vfork()进入do_fork()时, 因其clone_flags为 VFORK | CLONE_VM | SIGHLD, 所以只执行了copy_files()、copy_fs()以及copy_sighand 而copy_mm(),则因标志位CLONE_VM为1,只是...
  • 进程的创建fork vs vfork

    2020-02-12 18:13:12
    本节来学习Linux中进程是如何创建的,以及fork和vfork的区别。 在大学的时候操作系统课程中我们都学过如何去创建一个进程,是通过fork系统调用命令来创建的。 使用fork创建进程 如下是一个简单的通过fork系统...
  • 区别一:vfork可以直接使用父进程存储空间,不拷贝 区别二:vfork可以保证子进程先运行,当子进程调用exit退出后,父进程才执行 #include #include 函数原型: pid_t vfork(void); 下面看看这个demo #include #...
  • 不同点 : fork : 子进程会拷贝父进程数据段 vfork : 子进程与父进程共享数据段 fork : 子进程创建以后,父子进程执行顺序与系统调度算法有关,不确定 vfork : 子进程创建以后,子进程先运行,父进程后运行 注意...
  • [转载]fork()、vfork()及clone()fork()、vfork()和clone()分别通过系统INT0X80调用sys_fork()、sys_vfork()或sys_clone(),从下面的代码可以看出,它们都调用了do_fork(),唯一的区别是所传参数中clone_flags不同。...
  • fork与vfork的区别

    2020-06-30 17:27:06
    vfork( ):子进程与父进程共享数据段 2.fork ()父子进程的执行次序不确定 vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec 或exit 之后父进程才可能被调度运行。 3.vfork ...
  • fork和vfork的区别

    2021-12-21 22:02:16
    现在我们进一步介绍一下vfork并且介绍fork与vfork的区别。 1. 引入vfork 在了解过fork的读时共享 写时复制操作后我们知道,复制出的子进程拥有自己的虚拟空间,但是物理空间是与父进程共享的,直到父子进程对数据...
  • -> vfork() -> man 2 vfork 功能: vfork - create a child process and block parent //创建一个子进程并且让父进程阻塞。 头文件:#include #include 原型: pid_t vfork(void); 参数:无 返回值: 成功: 产生新...
  • --> vfork() --> man 2 vfork 功能: vfork - create a child process and block parent //创建一个子进程并且将父进程锁住 使用格式: #include #include pid_t vfork(void); 参数:无 返回值: 成功: 父进程: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,136
精华内容 6,054
关键字:

vfork

友情链接: ABS_Amesim.rar