linux进程控制(进程创建、终止、替换)

进程创建( fork 与 vfork)

pid_t fork (void):详细介绍fork
  • fork函数是一个非常重要的函数,它从已存在进程中创建一个新进程,新进程为子进程,而原进程为父进程。创建子进程用到写时拷贝实现。子进程拷贝父进程PCB中的数据(虚拟地址空间,页表),子进程是父进程的一个复制品。
    在这里插入图片描述
pid_t vfork(void):详细介绍vfork
  • 从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。创建的子进程与父进程共用同一个虚拟地址空间。vfork 创建子进程后会阻塞父进程(阻塞父进程是为了防止调用栈混乱),直到子进程退出或程序替换。
    在这里插入图片描述
fork 与 vofork 的区别:

不同:

  • fork(): 父子进程的执行次序不确定。
    vfork():保证子进程先运行,在它调用 exec(进程替换) 或 exit(退出进程)之后父进程才可能被调度运行。

  • fork(): 子进程拷贝父进程的地址空间,子进程是父进程的一个复制品。
    vfork():子进程共享父进程的地址空间。

相同:

  • fork 与 vfork 两个接口创建子进程,都是在内核中调用 clone 函数实现的。

进程终止

进程终止场景:
  • 正常退出,结果符合预期。
  • 正常退出,结果不符合预期。
  • 异常退出。
如何终止一个进程:
  • 在main()函数中return,退出前会刷新缓冲区

  • 调用 exit(int statu) 接口(参数:status 定义了进程的终止状态,父进程通过wait来获取该值 ),该接口是一个库函数接口,退出前会刷新缓冲区

  • 调用_exit(int statu) 接口(参数:status 定义了进程的终止状态,父进程通过wait来获取该值 ),该接口是一个系统调用接口,退出时直接释放所有资源

    PS:上面所有的返回值大小都是通过一个字节来存储的,例如:exit(-1) , return -1, _exit(-1), 在终端执行echo $?(查看上一条执行命令执行之后的返回结果)发现返回值 是255。

  1 #include<stdio.h>                                                                                             
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 int main()
  5 {                                           
  6   _exit(-1);
  7 }      

在这里插入图片描述

进程等待

什么是进程等待
  • 子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。为了避免僵尸进程的产生,采取进程等待:父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
进程等待的方法
阻塞接口:为了完成一个功能,发起调用,若当前不具备完成条件,则一直等待!直到完成后返回。
非阻塞接口:为了完成一个功能,发起调用,若当前不具备完成条件,则立即报错返回。
  • wait
    函数原型:pid_t wait(int*status),这是一个阻塞接口,等待任意一一个子进程退出,若没有子进程退出,则一直等待。其中子进程的返回值会传入到参数 statu 中。

    返回值: 成功返回被等待进程pid,失败返回-1。

    参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

  • waitpid
    函数原型:pid_ t waitpid(pid_t pid, int *status, int options);

    参数
    pid:

    • ①: Pid=-1,等待任一个子进程。与 wait 等效。
    • ②: Pid=指定子进程的pid。等待其进程ID与pid相等的子进程。如果没有当前指定的子进程 pid ,则会报错返回。

    status:

    options:

    • ①:设置为 0 时为阻塞操作,如果没有子进程退出,则会一直等待,直到子进程退出父进程才会退出。
    • ②:设置为 WNOHANG 时,为非阻塞操作。(对于 WNOHANG 来说。①:如果有子进程退出(子进程先于父进程退出),即正常返回,则返回值就是子进程的 pid; ②:如果有子进程,但是却还没有收集到子进程退出(父进程先于子进程退出),它也会立即返回,不会像wait那样永远等下去,则返回值 ==0;③:如果父进程中没有通过 fork()创建子进程,则会报错,返回值为 -1 )

    返回值

    • ①:正常返回(即返回值>0),返回的是退出子进程的 pid。
    • ②:如果设置了选项WNOHANG,而调用中 waitpid 发现没有已退出的子进程可收集,则返回0(即子进程没有退出的时候,返回值 == 0)
    • ③:如果出错,则等待返回-1(即返回值 < 0)

进程程序替换

替换原理
  • 一般我们在一个进程中调用 fork 创建出子进程,该子进程往往需要执行另一个程序,此时我们就需要调用 exec 函数。当进程调用一种 exec 函数时,该进程的执行程序完全替换了新进程,而新程序则从其 main 函数开始执行。注意:exec 并不创建新进程,所以前后子进程的 ID 并没有改变。exec 只是用一个新的程序替换了当前进程的正文、数据、堆和栈段。 PS:exec 函数族不仅能替换子进程,也能替换父进程,所有进程都能替换。
替换函数
  • 程序替换函数是一族函数,可以通过man命令进行查看。 其中有六种以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[]);//系统调用

path:路径
file:环境变量PATH路径下的执行程序
arg/argv[]:从第1个是真正的参数,第0个是要执行文件本身的名称
envp[]:环境变量

对于这些函数名来说:

  • 带 p 与 不带 p ( 表示path )的区别

    ①: 不带字母 p 的exec函数,第一个参数必须是程序的相对路径或绝对路径,例如”/bin/ls”或”./a.out”,而不能是”ls”或”a.out”。
    ②:带字母 p 的exec函数,可以不用写路径,自动从PATH中寻找需要执行的程序,但是仅限于环境变量PATH路径下的程序。如果写上路径也可以正常执行。

  • l(list:表示采用列表) 和 v(vector:表示采用数组)的区别

    ①: 带有字母 l 的exec函数,要求将新程序的每个命令行参数都当作一个参数传给它,最后一个参数应该是NULL。
    ②:带有字母 v 的函数,则应该先构造一个指向各参数的指针数组,然后将该数组的首地址当作参数传给它,数组中的最后⼀个指针也是NULL。

  • 带 e 与 不带 e ( 表示env )的区别

    ①:带 e 表示新程序自己来维护环境变量。
    ②:不带 e 表示 新程序使用当前环境变量。
    在这里插入图片描述

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。 如果调用出错则返回 -1 ,所以exec函数只有出错的返回值而没有成功的返回值。其中,只有 execve 是真正的系统调用,其它五个函数都是库函数调用,它们都调用 execve。

execl:

  6   //最后一个参数必须是NULL                                                                        
  7   if(execl("/bin/ls","ls","-a","-l",NULL) == -1)
  8   {                       
  9     perror("execl error");
 10     exit(-1);             
 11   }   

在这里插入图片描述
execlp:

 13  //最后一个参数必须是NULL  
 14   if(execlp("ls","ls","-a","-l",NULL) == -1)                                                                                     
 15   {
 16     perror("execl error");
 17     exit(-1);
 18   }

在这里插入图片描述
execle:

   21   char* env[] = {"MYTEST=123","HELLO=456",NULL}; //最后一个参数必须是NULL                                                                              
   22   if(execle("./env","./env",NULL,env) ==-1) //最后一个参数必须是NULL  
   23   {
   24     perror("execl error");
   25     exit(-1);
   26   }

在这里插入图片描述

  • execv:
   29   char* argv[]={"ls","-a","-l",NULL};//最后一个参数必须是NULL  
   30   if(execv("/bin/ls",argv))
   31   {                                                                                                                            
   32     perror("execl error");
   33     exit(-1);
   34   }

在这里插入图片描述
execvp:

   29   char* argv[]={"ls","-a","-l",NULL};//最后一个参数必须是NULL  
   30   if(execvp("ls",argv) == -1)                                                                                                  
   31   {
   32     perror("execl error");
   33     exit(-1);
   34   }

在这里插入图片描述
execvpe:

   29   char* argv[]={"./env",NULL};//最后一个参数必须是NULL  
   30   char* env[] = {"MYTEST=123","HELLO=456",NULL};//最后一个参数必须是NULL  
   31   if(execve("./env",argv,env) == -1)                                                                                           
   32   {
   33     perror("execl error");
   34     exit(-1);
   35   }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值