fork,vfork

进程: 1,一段程序供执行,可以与其他进程共用;
      2,一个专用的内核空间堆栈;
      3,在内核中有一个task_struct数据结构,即进程控制块,有了这个数据结构进程才能成为内核调度的最小单位接受内核调度。
      4,有独立的用户空间。
进程是资源分配的最小单位,线程是调度的最小单位;


一个进程或不同进程的多个文件描述字可以指向同一个系统打开文件表项,这相当于单次打开文件但是复制了多个文件描述字的情形。例如通过dup()复制或者fork()继承。所以判断两个文件描述字是否指向同一个文件的本质在于看他们的文件描述符内容是否相同。

dup函数是利用文件的描述符的下角标(就是open的返回值)找到文件描述符然后复制到一个新的项中,fork函数同样是复制父进程的文件描述符到它自己的进程文件表项中。本质上他们的文件描述符内容是相同的,所以他们指向的是同一个系统打开的文件表项。公用一套文件状态标签,文件偏移指针和vnode指针。也可是说他们在同一种状态下打开了同一文件。而两个不相关的进程打开同一个文件的时候,两个进程他们有各自的系统打开文件进程表项,拥有不同的文件状态标签和文件偏移指针。
理解下面的代码:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    } while(0)


int main(int argc, char *argv[])
{
    signal(SIGCHLD, SIG_IGN);
    printf("before fork pid = %d ", getpid());
    int fd;
    fd = open("test.txt", O_WRONLY);//父进程打开一个文件
    if (fd == -1)
        ERR_EXIT("open error");

    
    pid_t pid;
    pid = fork();
    if (pid == -1)
        ERR_EXIT("fork error");

    if (pid > 0)
    {
        write(fd, "parent", 6);//父进程往文件中写入内容
        sleep(1);//睡眠是为了避免孤儿进程的产生,保证子进程执行的时候,父进程没有退出
    }
    else if (pid == 0)
    {
        write(fd, "child", 5);//子进程往文件中写入内容
    }
    return 0;
}

这时候查看test.txt 内容为:parentchild但是如果将第43行去掉,结果可能就是childt这是因为父进程没有退出的时候,他们共用一个偏移量,父进程写完子进程接着写入。但是当父进程退出的时候,文件偏移量就移到了文件首,子进程的写入覆盖了父进程的内容。(从头至尾他们只用一个文件偏移量)





fork()生成的子进程会复制父进程的缓冲区,理解下面的代码:
int mian (void)
{
    pid_t pid;

    printf("fork before......");
    pid=fork();
    if(pid>0){
        printf("here is parents! ");
    }
    if(pid==0){
        printf("here is child! ");
    }

    return 0;
}
这段代码运行之后会打印两次 fork before...... 如果将其改为printf("fork before.... "}的话就只会打印一次,其原因就是在于 具有输出缓冲区的作用。如果不加的话fork before....会留在缓冲区,当fork的时候会复制到子进程中,所以尽管fork()之前只调用了一次printf()但由于父子进程均在自己的输出缓冲区当中包含他,当调用fork()之后父子进程执行各自的printf时,新输出的内容将在其后;加上的话 在fork之前就将其输出,清理了缓冲区,fork没有复制到缓冲区的内容当然不能打印啦。但是fork()创建子进程后,子进程和父进程的执行顺序取决于内核的调度算法。如果想要让父子进程按某种顺序执行的话,需要有显式的进程之间的同步。



vfork()函数:
使用fork()函数的时候,父子进程都有自己独立的进程空间,也就是子进程需要完完全全的拷贝父进程的地址空间,而如果子进程中执行exec的话,等于它被一个新的程序替换掉了,它根本不需要拷贝父进程的数据,所以就会造成地址空间的浪费,这时才引入了vfork,也就是vfork之后子进程在执行exec之前,是不会拷贝父进程的地址空间的,不管子进程有没有改写数据。所以子进程会改变父进程的内容。并且vfork有一个限制,子进程结束时必须利用exit()或者_exit()退出,而不是return。




dup()和dup2()
#include <unistd.h>
int dup(int old);
int dup2(int old,int new);

两个函数都是复制文件描述字到新文件描述字,注意他们复制的是文件描述字的内容。而参数是文件描述符的下角标。返回新的文件描述字。

原文地址:https://www.cnblogs.com/yiyutianran/p/3826680.html