fork和vfork,exec

一.fork:子进程是父进程的一个拷贝,子进程获得同父进程相同的数据,但是同父进程使用不同的数据段和堆栈段。

特点:调用一次,返回两次。成功则在父进程中返回子进程ID,在子进程中返回0。失败则返回-1。

返回后都执行fork之后的语句。

例如:创建4个子进程,每个子进程都输出自己的pid和父进程的pid

#include <stdio.h>
#include <unistd.h>
int main()
{
    int pid ;
    for(int i = 0;i < 4;i++)
    {
        pid = fork();
        if(pid == 0) break;
    }
    if(pid == 0)
    {
        printf("i am child mypid:%d,myparentpid:%d\n",getpid(),getppid());
    }else if(pid > 0)
    {
        printf("i am parent mypid:%d,myparentpid:%d\n",getpid(),getppid());
        while(1);
    }
    else
    {
     perror(
"call fork failed\n");
      exit(1);
  }
}

在for循环中我们要判断是否为子进程,是子进程的话要跳出。因为子进程将父进程的i值拷贝过来,然后从fork语句的下一句开始执行。即}就会继续创建子进程,直到系统满了才停止。

运行结果:

父进程和子进程的输出顺序时可以变化的

我们可以看到父进程的pid是bash,这个问题我们下面再讨论。

二.vfork 和exec

vfork 系统调用创建的进程共享其父进程的内存地址空间,但是并不完全复制父进程的数据段,而是和父进程共享其数据段。为了防止父进程重写子进程需要的数据,父进程会被 vfork 调用阻塞,直到子进程退出或执行一个新的程序。由于调用 vfork 函数时父进程被挂起,所以如果我们使用 vfork 函数替换 forkdemo 中的 fork 函数,那么执行程序时输出信息的顺序就不会变化了。

 exec:调用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[]);

execve才是真正的系统调用  ,其他的函数都是调用它执行。

如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值.

函数名虽然看起来难记,但每个字母都有自己的意义,了解之后就不难记了:

对于带字母p的函数:   如果参数中包含/,则将其视为路径名。 否则视为不带路径的程序名,在PATH环境变量的目录列表中搜索这个程序。

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

简洁版:

    l 命令行参数列表

    p 搜素file时使用path变量

    v 使用命令行参数数组

    e 使用环境变量数组,不使用进程原有的环境变量,设置新加载程序运行的环境变量

 

举例:

    char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};

    char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};   

    execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);    

    execv("/bin/ps",ps_argv);

    execle("/bin/ps","ps","-o","pid,ppid,pgrp,session,tpgid,comm",NULL,ps_envp);

    execve("/bin/ps",ps_argv,ps_envp);

    execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);    //第一个是程序名,第二个是命令行参数

    execvp("ps", ps_argv);      

上面说的app的父进程是bash ,我们在终端键入./app  是bash  fork一个新的进程,这个进程执行的代码时用exec执行我们键入的路径程序。

猜你喜欢

转载自www.cnblogs.com/Lune-Qiu/p/9267785.html