【Linux探索学习】第十九弹——历程替换:深入剖析操作系统中的历程替换机 ...

打印 上一主题 下一主题

主题 838|帖子 838|积分 2514

Linux学习条记:
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
媒介:
   在Linux操作系统中,历程替换(Process Replacement)是一个告急的概念,它允许程序通过系统调用用另一个程序替换当前历程的执行内容。这个操作通常是通过exec系列系统调用实现的。历程替换使得一个历程可以在不改变历程ID(PID)的情况下,执行不同的程序。理解和把握exec系列函数对于深入了解Linux历程管理、历程间通信和系统编程非常告急。
  
  本文将详细讲解Linux中历程替换的概念、exec系列函数的利用方法、干系的系统调用、常见的用法示例,以及历程替换的现实应用场景。同时还会提供丰富的代码示例,帮助大家更好地理解这些函数的利用。
  目录

1. 什么是历程替换?
历程替换的核心特点:
2. exec系列函数
2.1 execve() 函数
函数原型:
示例代码:利用 execve() 执行一个程序
2.2 execvp() 函数
函数原型:
示例代码:利用 execvp() 执行一个程序
2.3 execlp() 函数
函数原型:
示例代码:利用 execlp() 执行一个程序
2.4 execv() 函数
函数原型:
示例代码:利用 execv() 执行一个程序
2.5 execl() 函数
函数原型:
示例代码:利用 execl() 执行一个程序
3. exec系列函数的常见错误
3.1 返回值与错误处理
3.2 exec的限制
4. 总结


1. 什么是历程替换?

   在Linux中,历程替换是指一个历程通过调用exec系列函数来替换其当前的代码、数据、堆栈等内存区域,进而加载并执行新的程序。换句话说,历程替换使得一个正在运行的历程不再执行原来的程序,而是执行另一个程序。
  
  历程替换发生时,当前历程的内存空间会被新的程序映像所替换。需要注意的是,历程替换操作自己并不会改变历程的PID(历程ID)。也就是说,历程替换发生后,新的程序在执行时,历程ID仍旧是原历程的PID。(结合我们之前所讲的内容,历程替换改变的不是历程自己,而是页表对应的物理地址)
  历程替换的核心特点:

   

  • 内存空间替换:历程的内存(代码段、数据段、堆栈等)会被新的程序映像替换。
  • 历程ID稳定:纵然历程的执行内容发生了变化,历程的PID不会改变。
  • 文件描述符继续:除非显式关闭,历程的文件描述符会被继续到新程序中,并且它们的状态(如文件指针位置)也会被继续。
  这里内存空间替换更深层的其实是连接虚拟地址和物理地址之间页表指向的改变,关于页表的内容我们在前面讲历程地址空间时有讲过,作为拔高内容了解即可
历程替换通常与fork系统调用结合利用。在利用fork时,父历程会创建一个子历程,子历程继续父历程的状态(包括文件描述符、内存、环境变量等)。接着,子历程通过exec系列函数来替换自身的程序内容,执行新的任务。

2. exec系列函数

在Linux中,exec系列函数用于执行历程替换。它们会用新的程序替换当前历程的映像。exec系列函数有多个变种,常用的包括:
   

  • execve():最根本的系统调用,提供程序路径、参数数组和环境变量列表。
  • execvp():根据$PATH环境变量查找程序路径并执行。
  • execlp():与execvp()类似,但以参数列表的形式提供命令行参数。
  • execv():与execve()类似,但不查找$PATH,需要提供完备的程序路径。
  • execl():与execlp()类似,但以参数列表的形式传递命令行参数。
  我们可以通过man函数手册来查看:
  1. man exec
复制代码

2.1 execve() 函数

execve()是exec系列函数中最根本的函数,也是其他exec函数的底层实现。它允许一个历程加载并执行指定路径的程序,同时传递命令行参数和环境变量。
函数原型:

  1. int execve(const char *pathname, char *const argv[], char *const envp[]);
复制代码
  

  • pathname:要执行的程序的完备路径。
  • argv:一个字符串数组,包罗传递给程序的命令行参数,最后必须以NULL结尾。
  • envp:一个字符串数组,包罗程序的环境变量,最后也必须以NULL结尾。
  示例代码:利用 execve() 执行一个程序

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     char *args[] = {"ls", "-l", NULL};  // 命令行参数
  5.     char *env[] = {"PATH=/bin", NULL};  // 环境变量
  6.     printf("Before execve\n");
  7.    
  8.     if (execve("/bin/ls", args, env) == -1) {
  9.         perror("execve failed");
  10.     }
  11.     printf("This will not be printed if execve is successful.\n");
  12.     return 0;
  13. }
复制代码
在这个示例中,execve()会替换当前历程并执行ls -l命令。execve()调用成功后,原来的历程中的内容就会被替换,所以后续的printf语句将不会执行。

2.2 execvp() 函数

execvp()是execve()的一个更高层的封装。它根据环境变量$PATH来查找可执行文件并执行。这意味着你只需要指定可执行文件的名称,而无需提供完备的路径。
函数原型:

  1. int execvp(const char *file, char *const argv[]);
复制代码
  

  • file:要执行的程序的名称,系统会根据$PATH查找该程序。
  • argv:命令行参数数组,最后必须以NULL结尾。
  示例代码:利用 execvp() 执行一个程序

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     char *args[] = {"ls", "-l", NULL};  // 命令行参数
  5.     printf("Before execvp\n");
  6.     // 使用 $PATH 查找可执行文件并执行
  7.     execvp("ls", args);
  8.     printf("This will not be printed if execvp is successful.\n");
  9.     return 0;
  10. }
复制代码
在这个示例中,execvp()会根据$PATH查找ls命令并执行。如果execvp()调用成功,后续的printf语句将不会执行。

2.3 execlp() 函数

execlp()是execvp()的一个变种,它允许你直接传递命令行参数,而不需要构建一个参数数组。
函数原型:

  1. int execlp(const char *file, const char *arg, ...);
复制代码
  

  • file:要执行的程序的名称,系统会根据$PATH查找该程序。
  • arg:程序的第一个参数,紧跟着是程序的其他参数,最后一个参数必须是NULL。
  示例代码:利用 execlp() 执行一个程序

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     printf("Before execlp\n");
  5.     // 使用 $PATH 查找可执行文件并执行
  6.     execlp("ls", "ls", "-l", NULL);
  7.     printf("This will not be printed if execlp is successful.\n");
  8.     return 0;
  9. }
复制代码
在这个示例中,execlp()会查找ls命令并执行。与execvp()不同,execlp()是通过可变参数来传递命令行参数的,而不是利用数组。
运行效果:

2.4 execv() 函数

execv()是execve()的一个封装,允许你提供程序路径和参数数组。它不通过$PATH查找程序,而是需要提供完备的程序路径。
函数原型:

  1. int execv(const char *pathname, char *const argv[]);
复制代码
  

  • pathname:要执行的程序的完备路径。
  • argv:命令行参数数组,最后必须以NULL结尾。
  示例代码:利用 execv() 执行一个程序

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     char *args[] = {"ls", "-l", NULL};  // 命令行参数
  5.     printf("Before execv\n");
  6.     // 使用完整路径执行命令
  7.     execv("/bin/ls", args);
  8.     printf("This will not be printed if execv is successful.\n");
  9.     return 0;
  10. }
复制代码
在这个示例中,execv()会用完备路径/bin/ls来执行命令。如果execv()调用成功,后续的printf语句将不会执行。
运行效果:

2.5 execl() 函数

execl()是execlp()的一个变种,利用可变参数的方式来传递命令行参数。
函数原型:

  1. int execl(const char *pathname, const char *arg, ...);
复制代码
  

  • pathname:要执行的程序的完备路径。
  • arg:程序的第一个参数,后面是其他参数,最后一个参数必须是NULL。
  示例代码:利用 execl() 执行一个程序

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main() {
  4.     printf("Before execl\n");
  5.     // 执行命令并传递参数
  6.     execl("/bin/ls", "ls", "-l", NULL);
  7.     printf("This will not be printed if execl is successful.\n");
  8.     return 0;
  9. }
复制代码
在这个示例中,execl()会执行/bin/ls命令,并传递参数-l。
运行效果:

3. exec系列函数的常见错误

尽管exec系列函数非常强盛,但它们也有一些常见的错误,需要注意。
3.1 返回值与错误处理

exec系列函数调用成功时,不会返回控制权给调用者。它们会替换当前历程的映像,新的程序开始执行。因此,如果exec调用成功,后面的代码将不会执行。如果调用失败,exec函数会返回-1,并设置errno,你可以通过perror()或strerror()函数来输出错误信息。
常见的错误包括:
   

  • ENOENT:文件不存在。指定的可执行文件无法找到。
  • EACCES:权限不足。没有足够的权限来执行指定的文件。
  • ENOMEM:内存不足。系统无法为新的程序分配足够的内存。
  3.2 exec的限制

   

  • exec替换时会丢失历程的原始状态。当前历程的栈、堆、全局变量等都会被新的程序内容覆盖。如果你需要保存某些状态(如打开的文件描述符),应在调用exec前举行适当的保存。
  • exec不会返回,如果你需要在exec失败时处理错误,必须在调用后查抄返回值。
  4. 总结

历程替换是Linux中一个非常告急的概念,exec系列函数提供了在运行时替换当前历程的本领。通过execve()、execvp()、execlp()、execv()和execl()等函数,我们可以灵活地执行不同的程序,而不需要创建新的历程。
历程替换通常与fork结合利用,fork创建一个新历程,而exec替换子历程的程序映像。这种模式广泛应用于Shell的实现、任务调度、历程间通信等领域。
函数描述execve执行指定路径的程序,传递命令行参数和环境变量。execvp根据$PATH查找可执行文件并执行,传递参数。execlp与execvp类似,但以参数列表的形式传递命令行参数。execv与execve类似,但不查找$PATH,需要提供完备路径。execl与execlp类似,但以参数列表的形式传递命令行参数。 理解并纯熟利用exec系列函数,是编写高效、灵活的系统程序的关键之一。盼望本文通过丰富的代码示例和详细的表明,能够帮助你更好地把握Linux中的历程替换机制。
本篇条记:


感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表