【Linux】进程替换

(一)进程替换

  • 进程替换不会创建新的进程,进程PCB未发生改变,进程实体(数据代码内容)被替换
  • 进程替换成功后不会执行替换函数下的代码,失败后会执行
  • 进程替换成功不返回,失败后返回-1

(二)环境变量

  • 环境变量的作用:

当使用shell来运行一个程序时,若没有加绝对路径,系统先会在当前路径下寻找该程序,若没找到就会去环境变量中去寻找该程序。都没找到就会报没有该指令的错误。

jiege@ubuntu:~/Desktop/code/c$ pwd
/home/jiege/Desktop/code/c
jiege@ubuntu:~/Desktop/code/c$ which gcc
/usr/bin/gcc
jiege@ubuntu:~/Desktop/code/c$ echo $PATH
/home/jiege/tools/nodejs/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
jiege@ubuntu:~/Desktop/code/c$ 

  • 查看所有的环境变量
#include <stdio.h>

int main(int arg, char* argv[], char* envp[])
{
    
    
	int i = 0;
	while(envp[i] != NULL)
	{
    
    
		printf("%s\n", envp[i++]);
	}

	return 0;
}

结果:

请添加图片描述


(三)进程替换API(unistd.h)

exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:

  • l : 使用参数列表
  • p:使用文件名,并从PATH环境进行寻找可执行文件
  • v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
  • e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量

(1)系统调用API

 #include <unistd.h>

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

(2)库函数API

  • int execl(const char* pathname, const char* arg, .../*(char*)NULL*/);

    参数解释:目标程序的进程替换

    • pathname:目标程序的路径
    • arg:执行这个程序的方式
    • 返回值:失败-1
    • 例如:execl("/bin/ls", "ls" "-a", "-l", (char*)NULL);

  • int execlp(const char *file, const char *arg, .../* (char*) NULL */);

    参数解释:该函数会在环境变量的路径中查找file

    • file:要执行的目标程序
    • arg:传给目标程序的参数
    • 返回值:失败-1
    • 例如:execlp("ls", "ls", "-a", "-l", (char*)NULL);
    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
           
           
    	printf("hello\n");
    
    	execlp("ls", "ls", "-a", "-l", (char*)NULL);
    	printf("world\n");
    	return 0;
    }
    
    • 结果:
    jiege@ubuntu:~/Desktop/test$ vim main.c
    jiege@ubuntu:~/Desktop/test$ gcc -o main main.c
    jiege@ubuntu:~/Desktop/test$ ./main
    hello
    总用量 24
    -rwxrwxr-x 1 jiege jiege 16736 129 20:12 main
    -rw-rw-r-- 1 jiege jiege   148 129 20:12 main.c
    jiege@ubuntu:~/Desktop/test$ 
    

  • int execle(const char *pathname, const char *arg, .../* (char*) NULL, char *const envp[] */);

    参数解释:给这个目标进程传入指定envp的环境变量

    • pathname:目标程序的路径

    • arg:替换后如何执行的方式,envp表示要导入的环境变量

    • 例:

      • 程序myenv输出未设置环境变量的变量
      #include <stdio.h>
      #include <stdlib.h>
      
      int main()
      {
               
               
      	printf("myenvp: %s\n", getenv("MYENVP"));
      	return 0;
      }
      
      • 结果

      请添加图片描述

    • 程序b再使用进程替换给a程序传入环境变量
    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
           
           
    	char* buff[] = {
           
           "MYENVP=/home/jiege/awei", NULL};
    	execle("./myenv", "myenv", NULL, buff);
    	perror("execle err");
    	return 0;
    }
    
    • 结果

    请添加图片描述

    注意:envp数组前的参数是NULL,envp数组最后一个元素为NULL,并且该操作会覆盖原环境变量的值


  • int execv(const char *pathname, char *const argv[]);

    参数详解:

    • pathname:目标程序路径名/usr/...
    • argv:将所有的执行方式存放在argv指针数组中
    • 例:
    #include <stdio.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[])
    {
           
           
    	char* buff[] = {
           
           "ls", "-l", NULL};
    
    	execv("/usr/bin/ls", buff);
    	perror("execv err");
    	return 0;
    }
    
    • 结果:

    请添加图片描述


  • int execvp(const char *file, char *const argv[]);

    参数详解:给目标程序传入参数

    • file:被执行的目标程序

    • argv:传给file程序的参数

    • 例:使用程序a中替换成程序b,b将得到的参数打印

      • a.c

        #include <stdio.h>
        #include <unistd.h>
        
        int main()
        {
                   
                   
        	printf("A start execvp\n");
        	char* argv[] = {
                   
                   "hello", "world", NULL};
        
        	execvp("./b", argv);
        	perror("execvp err");
        	return 0;
        }
        
      • b.c

        #include <stdio.h>
        #include <unistd.h>
        
        int main(int argc, char* argv[])
        {
                   
                   
        	int i = 0;
        	while(argv[i] != NULL)
        	{
                   
                   
        		printf("%s\n", argv[i++]);
        	}
        	return 0; 
        }
        
    • 结果:

    请添加图片描述


  • int execvpe(const char *file, char *const argv[], char *const envp[]);

    参数详解:

    • file:目标程序
    • argv:函数参数
    • envp:环境变量

(四)进程替换和fork的结合使用案例

  • 一个例子:就比如bash窗口中输入ps -f指令,查看当前进程的完整格式
jiege@ubuntu:~$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
jiege       2680    2669  0 14:28 pts/0    00:00:00 bash
jiege      11269    2680  0 17:07 pts/0    00:00:00 ps -f
jiege@ubuntu:~$ 
  • 分析:

    可以看到ps -f这个进程的进程号PID是11269,它的父进程的进程号PPID是2680;而2680就是bash这个进程。

  • 结论:

    bash就是这个shell窗口的进程的名字,当你输入ps -f时,bash这个进程就fork一个子进程,子进程进程替换执行ps -f命令,将此时的结果输出给父进程bash,父进程输出打印结果

(五)进程替换测试

  • 代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    
    
	printf("pid of main: %d\n", getpid());

	//进程替换
	execl("/bin/ps", "ps", "-f", (char*)NULL);
	perror("exec err");
	
	exit(0);
}
  • 执行结果
jiege@ubuntu:~/Desktop/code/exec$ ./main
pid of main: 11556
UID          PID    PPID  C STIME TTY          TIME CMD
jiege       9970    2669  0 14:28 pts/1    00:00:00 bash
jiege      11556    9970  0 17:50 pts/1    00:00:00 ps -f
jiege@ubuntu:~/Desktop/code/exec$ 

  • 结果分析:

    • 在bash中执行./main时,bash进程fork出子进程,子进程替换成main程序,main程序中再进行进程替换成ps -f程序

(六)进程替换API总结

函数名 参数传递形式 路径 是否导入环境变量
execl 列表 需要可执行程序路径 不导入 使用当前环境变量
execlp 列表 默认在环境变量中找 不导入 使用当前环境变量
execle 列表 需要可执行程序路径 导入 使用导入的环境变量
execv 数组 需要可执行程序路径 不导入 使用当前环境变量
execvp 数组 默认在环境变量中找 不导入 使用当前环境变量
execve 数组 需要可执行程序路径 导入 使用导入的环境变量

猜你喜欢

转载自blog.csdn.net/xiaoxiaoguailou/article/details/121847433