Linux:带你理解程序替换


程序替换

简单来看: 就是替换一个进程正在运行的程序

替换一个pcb映射在内存中的代码和数据(加载另一个程序到内存中,然后更新页表信息,初始化虚拟地址空间),这个进程pcb将从头开始调度新的进程开始运行( 说白了就是让这个进程运行另一个程序)(pcb不变)

注意:

程序替换之后,当前进程运行完替换后的程序就会退出,并不会又回去运行原先的程序

替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

在这里插入图片描述

替换函数

其实有六种以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[]);
  • 将新程序的运行参数,通过不定参的形式传递进入新的程序函数,以NULL结尾

  • path:带有路径的新程序名称,就是使用这个程序替换进程正在调度运行的程序

  • const char * arg:是程序的运行参数

  • 事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册第2节,其它函数在 man手册第3节。这些函数之间的关系如下图所示。

在这里插入图片描述

exec函数区别

  1. l 和 v 的区别:

程序运行参数的赋予方式不同

  • l通过不定参完成
  • v通过字符串指针数组进行赋予
  1. 带p和不带p的区别

第一个参数加载的新程序名称是否需要带路径,

  • 带p则不需要,只要文件名即可,默认回去在PATH环境变量指定的路径下查找文件
  • 不带p则需要加路径
  1. 带e和不带e的区别

这个进程的环境变量是否需要重新初始化

  • 有e则表示初始化
  • 若没有e则表示使用默认的环境变量

函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值。
    • 为什么调用成功没有返回值?
      因为调用exec成功,则当前进程的代码和数据已经被替换,以前的堆和栈已经被销毁,返回值也保存在栈中,栈已经被销毁,则返回值不存在。
  • 调用exec并不创建新进程,所以调用exec前后,进程的pid不变。

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

在这里插入图片描述

exec调用举例如下:

#include <unistd.h>

int main() {
	char *const argv[] = {"ps", "-ef", NULL};
	char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};

	execl("/bin/ps", "ps", "-ef", NULL);
	execv("/bin/ps", argv);

	// 带p的,可以使用环境变量PATH,无需写全路径 
	execlp("ps", "ps", "-ef", NULL);
	// 带e的,需要自己组装环境变量 
	execle("ps", "ps", "-ef", NULL, envp);
   
	// 带p的,可以使用环境变量PATH,无需写全路径 
	execvp("ps", argv);
	// 带e的,需要自己组装环境变量 
	execve("/bin/ps", argv, envp);
	
	exit(0); 
}

实现一个minishell

下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。
在这里插入图片描述

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序并等待这个进程结束。 所以要写一个shell(shell就是一个命令行解释器),需要循环以下过程:

  1. 等待用户的标准输入 [ls -l -a]
  2. 对用户数据进行解析,得到程序名称 [ls] 以及参数信息[-l] 和[-a]
  3. 建立一个子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程退出(wait)

实现代码

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

// 命令行
char command[1024];

int do_face() {
   	// 初始化命令行缓冲区 --- 全局变量默认初始化为 NULL
   	memset(command, 0x00, 1024);
   	printf("[user@localhost]$ ");
   	
   	// 刷新缓冲区 --- 将缓冲区中的内容输出到屏幕上
   	fflush(stdout);
   	
   	// %[^\n]: 接收到 '\n' 就停止接收
   	// %*c 接收最后一个字符('\n')但是不赋予 command
   	if (scanf("%[^\n]%*c", command) == 0) {
    	// 如果只输入 '\n' 则将 '\n' 吃掉
   		getchar();
    	return -1;
  	}
  	return 0;
}

// 将命令行中的字符串切割成各个命令
char** do_parse(char* cmd) {
	// 保存各个字符串命令
  	static char* argv[32];
  	// 下标
  	int argc = 0;
  	char* cur = cmd;
  	
  	while (*cur != '\0') {
    	if (*cur != ' ') {
      		argv[argc++] = cur;
      		while (*cur != ' ' && *cur != '\0') {
        		++cur;
      		}
      		if (*cur != '\0') {
        		*cur = '\0';
        		++cur;
      		}
    	}
    	while (*cur != '\0' && *cur == ' ') {
      		++cur;
    	}
  	}
  	// 在最后一个命令后面加 NULL
  	argv[argc] = NULL;
  	return argv;
}

// 进程替换, 让子进程来完成shell调用各个命令的任务
void do_exec() {
  	pid_t pid = fork(); 
  	
  	char** argv = {NULL};
  	if (pid < 0) {
    	perror("fork error");
    	exit(-1);
  	} 
  	else if (pid == 0) {
    	argv = do_parse(command);
    	// 如果命令行不为空, 则进程替换
    	if (argv[0] != NULL) {
      		execvp(argv[0], argv);
    	} 
    	else {
      		exit(0);
    	}
  	} 
  	else {
    	// 进程等待
    	waitpid(pid, NULL, 0);
  	}
}

int main() {
  	while (1) {
    	if (do_face() < 0) {
      		continue;
    	}
    	do_exec();
  	}
  	return 0;
}                              

如有不同见解,欢迎留言讨论~~

猜你喜欢

转载自blog.csdn.net/AngelDg/article/details/106732935
今日推荐