fork和vfork解析

linux操作系统中,这两个函数都是用来创建子进程

# include <unistd.h>

pid_t fork(void);
pid_t vfork(void);


成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为无符号整型。

失败:返回 -1。

fork和vfork的区别

fork特点:fork创建子进程的时候是完全拷贝一份父进程的资源,子进程独立于父进程

fork优点:父子进程相互独立,子进程对父进程中同名变量进行修改并不会影响其在父进程中的值。

vfork特点:vfork创建的子进程和父进程共享地址空间,子进程完全运行在父进程的地址空间上,子进程的修改同样对父进程可见,用                            vfork创建子进程后,父进程会被阻塞,直到子进程调用exec(进程替换)或exit(进程退出);

vfork优点:vfork避免了(fork函数子进程被创建后,仅仅为调用exec执行另一个程序,它对地址空间的复制是多余的)这个问题,减少                       了不必要的开销。

vfork保证子进程先运行,它调用exec或exit后父进程才能调度运行,fork的父子进程运行顺序不定,取决于内核的调度算法。

父进程中的数据空间和堆、栈可能会产生副本,具体情况要看使用的是fork还是vfork,fork产生副本,vfork则共享这部分内存。
 

另外linux还有第三个创建进程的系统调用,Linux上创建线程一般使用的是pthread库 实际上linux也给我们提供了创建线程的系统调用,就是clone,linux中的pthread_create最终调用clone

clone函数功能强大,带了众多参数,因此由他创建的进程要比前面2种方法要复杂。

clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。

扫描二维码关注公众号,回复: 3930785 查看本文章

先有必要说下这个函数的结构 :
int clone(int (fn)(void ), void *child_stack, int flags, void *arg); 
 

exec族函数:

exec 族函数一共有六个:

#include <unistd.h>

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);


例如:

/* 执行 /bin/ls -al /ect/passwd */

// 执行/bin目录下的ls, 第一参数为程序名ls, 第二个参数为"-al", 第三个参数为"/etc/passwd"

execl("/bin/ls", "ls", "-al", "/etc/passwd", (char *) 0);

函数字中带字母 "l" 的表示其参数个数不确定,带字母 "v" 的表示使用字符串数组指针 argv 指向参数列表。
函数名字中含有字母 "p" 的表示可以自动在环境变量 PATH 指定的路径中搜索要执行的程序。
函数名字中含有字母 "e" 的函数比其它函数多一个参数 envp。该参数是字符串数组指针,用于指定环境变量。调用这样的函数时,可以由用户自行设定子进程的环境变量,存放在参数 envp 所指向的字符串数组中

事实上,只有 execve 是真正的系统调用,其它五个函数最终都调用 execve。这些函数之间的关系如下图所示:

exec 族函数的特征:调用 exec 族函数会把新的程序装载到当前进程中。在调用过 exec 族函数后,进程中执行的代码就与之前完全不同了,所以 exec 函数调用之后的代码是不会被执行的。

例子:

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

int main(void)
{
    pid_t pid;

    if((pid = vfork()) < 0)
    {
        printf("vfork error!\n");
        exit(1);
    }
    else if(pid == 0)   //子进程
    {
        printf("Child process PID: %d.\n", getpid());

        char *argv[ ]={"ls", "-al", "/home", NULL};  
        char *envp[ ]={"PATH=/bin", NULL};
        if(execve("/bin/ls", argv, envp) < 0)
        {
            printf("subprocess error");
            exit(1);
        }

        // 子进程要么从 ls 命令中退出,要么从上面的 exit(1) 语句退出
        // 所以代码的执行路径永远也走不到这里,下面的 printf 语句不会被执行
        printf("You should never see this message.");
    }
    else    //父进程
    {
        printf("Parent process PID: %d.\n", getpid());
        sleep(1);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/q2519008/article/details/83342001