Linux:进程终止和进程替换

时间:2024-07-08 21:18:34

Linux:Linux:进程终止和进程替换

  • 一、进程终止
    • 1.1 进程退出场景和创建退出方式
  • 1.2 exit 和 _exit区别
  • 二、进程程序替换
    • 2.1 进程替换函数
    • 2.2 函数解释及命名解释
      • 函数解释
      • 命名解释
    • 2.3 单进程程序替换(无子进程)
      • 2.3.1 带`l`函数进程替换(execl为例)
      • 2.3.2 带`p‘函数进程替换(execlp为例)
      • 2.3.3 execv、execvp替换函数应用实例
    • 2.4 进程替换其他程序,调用运行其他语言程序
  • 三、进程替换时环境变量的继承
    • 3.1 进程替换时,子进程环境变量由来
      • 3.2 为何父子进程间环境变量的继承不受进程替换的影响
      • 3.3 子进程获取环境变量的3种方式

一、进程终止

1.1 进程退出场景和创建退出方式

 进程退出有3种场景:代码执行完毕,结果正确;代码执行完毕,结果错误;代码异常终止!

 而进程退出的常见方式主要分为以下两大类:

正常终止
从main返回
调用exit退出
调用_exit退出
异常终止 ctrl c, 信号终止

1.2 exit 和 _exit区别

  exit 和 _exit都可以直接终止进程。但_exit是系统调用接口,而exit为函数调用,底层封装了exit。
不同的是,exit终止进程时,会刷新缓冲区,执行用户的清理函数,关闭流等操作;而_exit则是直接“粗暴”的退出进程,不做任何其他工作!!

在这里插入图片描述

二、进程程序替换

 fork()创建子进程时,子进程执行的代码和数据都是父进程的一部分。如果我们想让子进程执行全新的代码,访问全新的数据,我们可以采用一种技术 —— 程序替换!而进程替换可以将命令行参数和环境变量传递给被替换程序的main()函数参数!!

2.1 进程替换函数

 进程替换函数有6种以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 execvpe(const char *file, char *const argv[], char *const envp[]);

 而exec函数底层都封装了系统调用接口execve的疯转,以实现不同的需求!

 #include <unistd.h>

 int execve(const char *filename, char *const argv[], char *const envp[]);

2.2 函数解释及命名解释

函数解释

  1. 如果这些函数调用成功,也就意味着进程替换成功。此时重新加载新的程序,从启动代码开始执行,并且不再返回。即进程替换后,执行新程序,执行完后直接退出!!
  2. 如果进程替换函数执行失败,此时返回值设为-1。
  3. exec函数只有出错的返回值,没有成功的返回值。

命名解释

  • l(list):参数采用列表形式。
  • v(vector):参数采用数组。
  • p(path):带p表示执行程序时,OS会自带去环境变量PATH中查找路径。
  • e(env):表示自己维护环境变量。

2.3 单进程程序替换(无子进程)

2.3.1 带l函数进程替换(execl为例)

 下面我们在一段代码的开头和结尾分别输出打印相关信息,然后两段信息输出代码直接调用execl替换ls -a-l

【源代码】:

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


int main()
{
    printf("pid: %d, ecec command begin!\n", getpid());
    
    //进程替换,执行ls指令相关程序
    execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    
    printf("pid: %d, ecec command end!\n", getpid());

    return 0;
}

【运行结果】:

在这里插入图片描述

【函数参数原型解释】:


在这里插入图片描述



在这里插入图片描述


2.3.2 带`p‘函数进程替换(execlp为例)

【源代码】:(头文件省略)

int main()
{
    printf("pid: %d, ecec command begin!\n", getpid());
    execlp("pwd", "pwd", NULL);
    printf("pid: %d, ecec command end!\n", getpid());
    return 0;
}

【运行结果】:

在这里插入图片描述

【函数参数原型解释】:
在这里插入图片描述
在这里插入图片描述

2.3.3 execv、execvp替换函数应用实例

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


int main()
{
    char *const argv[] = {
        "ls",
        "-l",
        "-a",
        NULL
    };
    printf("pid: %d, ecec command begin!\n", getpid());// 和execl、execlv类似,只不过下面函数时通过指针数组的方式,指明替换程序的执行方式!!
    execv("/usr/bin/ls", argv);
    execvp("ls", argv);
    
    printf("pid: %d, ecec command end!\n", getpid());

    return 0;
}

2.4 进程替换其他程序,调用运行其他语言程序

 上述所有的程序替换都是替换系统指令程序,那如何替换自己写的程序。
 下面我们在c程序中创建子进程,让子进程发送进程替换一段c++可执行程序,并且父进程等待子进程!!

【待替换C++程序】:

#include <iostream>

int main()
{
	std::cout << "hello c++!" << std::endl;
    std::cout << "hello c++!" << std::endl;
    std::cout << "hello c++!" << std::endl;
    return 0;
}

【主代码C程序】:

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

extern char **environ;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("pid: %d, ecec command begin!\n", getpid());
     	execl("./mytest", "mytest");//替换C++程序
     	//替换失败,执行下面代码
        printf("pid: %d, ecec command end!\n", getpid());
        exit(1);
    }
   
    pid_t rid = waitpid(-1, NULL, 0);
    if(rid == id)
    {
        printf("wait pid: %d success!!!\n", rid);
    }
    return 0;
}

【运行结果】:

在这里插入图片描述

  • 为啥在c程序中,可以直接替换c++程序?根本原因在于exec函数发生的是进程替换,任何语言程序一旦运行起来就变成了进程,便可发生进程替换。系统大于一切!!

三、进程替换时环境变量的继承

3.1 进程替换时,子进程环境变量由来

 环境变量是数据,所有可以通过地址空间实现父子间通过写时拷贝的方式共享数据 —— 环境变量。所以当通过exec函数进行进程替换时,子进程的环境变量是直接从父进程来的。

3.2 为何父子进程间环境变量的继承不受进程替换的影响

 父进程和子进程间通过写时拷贝的方式,让环境变量别子进程继承,从而实现环境变量的全局性!但为何调用exec函数进行进程替换后,环境变量没有发生修改,变为被替换程序的环境变量?

 原因很简单,程序替换,只替换新程序的代码和数据,环境变量不会被替换!!

3.3 子进程获取环境变量的3种方式

  1. 操作系统直接将环境变量传递给子进程,子进程直接用。或者直接通过execle、execvpe的最后一个参数直接传递给子进程!

【实例】:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        execle("./mytest", "mytest", NULL, environ);//进程替换,直接将environ传递给子进程
        exit(1);
    }
   
    pid_t rid = waitpid(-1, NULL, 0);
    if(rid == id)
    {
        printf("wait pid: %d success!!!\n", rid);
    }
    return 0;
}
  1. 直接构造自己的环境变量表传递给子进程。

【实例】:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        execle("./mytest", "mytest", NULL, environ);//进程替换,直接将environ传递给子进程
        exit(1);
    }
   
    pid_t rid = waitpid(-1, NULL, 0);
    if(rid == id)
    {
      	char *const envp[] = {
         	"MYENV1 = 111111111111111111",
        	"MYENV2 = 111111111111111111",
        	"MYENV3 = 111111111111111111",
       		 NULL
    	};
        execle("./mytest", "mytest", NULL, envp);//直接将自己构造的函数变量表envp传递给子进程
     }
    return 0;
}
  1. 借助putenv(),新增环境变量给父进程然后传递给子进程

【实例】:

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

extern char **environ;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        char *myenv = "MYENV = 111111111111111111";
        putenv(myenv);//将环境变量MYENV添加到父进程环境变量表中
        
        execl("./mytest", "mytest");//直接传递给子进程
        exit(1);
    }
   
    pid_t rid = waitpid(-1, NULL, 0);
    if(rid == id)
    {
        printf("wait pid: %d success!!!\n", rid);
    }
    return 0;
}