Linux - 进程控制:进程创建、进程终止、进程等待及进程程序替换 ...

打印 上一主题 下一主题

主题 542|帖子 542|积分 1626

目录
进程创建
  fork函数初识
  fork函数返回值
  写时拷贝
  fork常规用法
  fork调用失败的原因
进程终止
  进程退出场景
  进程退出码
  进程正常退出
        return退出
        exit函数
        _exit函数
  return、exit和_exit之间的区别与联系
  进程异常退出
进程等待
  进程等待的须要性
  获取子进程status
  进程等待的方法
  wait方法
  waitpid方法
  多进程创建以及等待的代码模型
  基于非阻塞接口的轮询检测方案
进程程序替换
  替换原理
  替换函数
  函数表明
  命名明确


进程创建

  fork函数初识

        在Linux系统中,fork函数是一个至关紧张的功能,它用于从一个已有的进程中生成一个新的进程。生成的新进程称为子进程,而原本的进程则称为父进程。
        返回值表明: 在子进程中,fork函数会返回0;在父进程中,它返回子进程的进程ID(PID)。假如子进程的创建失败,则函数会返回-1。
当一个进程调用 fork 时,控制权会转移到内核中的 fork 代码,内核会执行以下操作:

  • 为子进程分配新的内存块和内核数据结构。
  • 将父进程的一部门数据结构内容复制到子进程中。
  • 将子进程添加到系统进程列表中。
  • fork 返回,并启动调度器举行进程调度。
        fork 之后,父进程和子进程共享相同的代码段。这意味着在两者中执行的指令是相同的,但它们拥有独立的执行流和数据空间。以下是一个例子:

代码效果:

        可以看到,Before 只输出了一次,而 After 则输出了两次。这里,Before 是由父进程打印的,而调用 fork 函数后打印的两个 After,分别由父进程和子进程各自执行。这意味着,在 fork 之前,只有父进程在独立执行;而在 fork 之后,父进程和子进程分别在两个独立的执行流中运行。
   注意:fork 之后,父进程和子进程的执行顺序完全由调度器决定,因此无法包管谁会先执行。
    fork函数返回值

   fork函数为什么要给子进程返回0,给父进程返回子进程的PID?
          fork 函数之所以在子进程中返回 0,而在父进程中返回子进程的 PID,是由于它们在进程间的角色和需求差别。
          对于子进程而言,它只有一个父进程,并且不须要特别标识这个父进程,因此返回值为 0 就足够了。这使得子进程可以通过判断返回值是否为 0 来确定自己是子进程。
          对于父进程来说,它大概会创建多个子进程,因此须要一个方式来区分和管理这些子进程。fork 返回子进程的 PID,可以让父进程明确地知道每个子进程的身份。父进程须要子进程的 PID 来执行一些特定的操作,好比等待子进程完成使命(利用 wait 系统调用),或者发送信号等。如许,父进程可以或许有用地管理和和谐其创建的子进程。
    为什么fork函数有两个返回值? 
          在父进程调用 fork 函数后,为了创建子进程,fork 函数内部会举行一系列操作,包罗:
  

  • 创建子进程的进程控制块(PCB):这是一个数据结构,用于存储子进程的状态信息和管理信息,如进程ID(PID)、进程状态、寄存器内容等。
  • 创建子进程的进程地址空间:这涉及为子进程分配独立的内存空间,使其拥有自己的代码段、数据段和堆栈段,尽管这些段的内容最初是从父进程复制过来的。
  • 创建子进程对应的页表:页表是内存管理的紧张结构,用于映射假造地址到物理地址。子进程须要自己的页表,以确保其内存访问的独立性。
          完成这些步调后,操作系统还会将子进程的进程控制块添加到系统的进程列表中。此时,子进程的创建过程就完成了,它成为系统中的一个独立进程,可以被调度执行。
  

        在 fork 函数内部执行 return 语句之前,子进程的创建过程就已经完成了。此时,子进程和父进程都已经存在,并且各自有独立的执行流。因此,fork 函数的返回不仅发生在父进程中,也在子进程中。
        正由于如此,fork 函数有两个返回值:在父进程中,它返回子进程的 PID;在子进程中,它返回 0。这两个差别的返回值资助区分父进程和子进程,使得程序可以根据差别的返回值执行差别的逻辑。例如,父进程可以继续管理子进程,而子进程则可以执行特定的使命。这种计划使得进程间的和谐和控制变得更加灵活和有用。
  写时拷贝

        在子进程刚刚创建时,父进程和子进程的代码及数据是共享的。这意味着父进程和子进程通过页表映射到相同的物理内存区域。只有当父进程或子进程实验修改数据时,系统才会将父进程的数据复制到一个新的内存区域,然后在新的位置上举行修改。

        这种在须要举行数据修改时才举行拷贝的技术称为写时拷贝(Copy-On-Write, COW)技术。 
   1、为什么数据要举行写时拷贝?
          进程具有独立性。在多进程情况中,每个进程须要独占各种资源,确保在多个进程同时运行时,它们之间互不干扰子进程的修改不能影响到父进程,以保持各进程的独立性和稳固性。
  
  2、为什么不在创建子进程的时间就举行数据的拷贝?
          子进程不肯定会利用父进程的全部数据。因此,在子进程未对数据举行写入的情况下,没有须要提前对数据举行拷贝。我们应当接纳按需分配的策略,即仅在须要修改数据时才举行拷贝(延时分配)。这种方法可以高效地利用内存空间
  
  3、代码会不会举行写时拷贝?
          虽然在90%的情况下,子进程不会修改父进程的数据,但这并不意味着代码无法举行写时拷贝。例如,在举行进程替换时,系统须要举行代码的写时拷贝,以确保进程的正确性和稳固性。
    fork常规用法



  • 一个进程大概盼望复制自己,以便子进程可以或许同时执行差别的代码段。例如,父进程可以在等待客户端请求时创建一个子进程,来处理这些请求。
  • 一个进程须要执行差别的程序。在这种情况下,子进程在从 fork 返回后,会调用 exec 函数来执行新的程序。
  fork调用失败的原因

fork 函数创建子进程时也大概会失败,主要有以下两种情况:


  • 系统中存在过多进程,导致内存空间不足,从而使子进程创建失败。
  • 现实用户的进程数凌驾了系统设置的限定,此时子进程创建也会失败。
进程终止

  进程退出场景

进程退出通常有三种情况:

  • 代码运行完毕且效果正确
  • 代码运行完毕但效果不正确
  • 代码异常终止,即进程瓦解
  进程退出码

        我们知道 main 函数是程序的入口点,但现实上 main 函数只是用户级代码的入口。main 函数自己也是由其他函数调用的。例如,在 Visual Studio 2013 中,main 函数是由名为 __tmainCRTStartup 的函数调用的,而 __tmainCRTStartup 函数又是通过加载器由操作系统调用的。换句话说,main 函数是间接由操作系统调用的。
        既然 main 函数是间接由操作系统调用的,那么当 main 函数执行完毕时,应当向操作系统返回相应的退出信息。这些退出信息是通过 main 函数的返回值作为退出码返回给操作系统的。通常情况下,返回值为0表示程序乐成执行完毕,而非0表示程序执行过程中出现了错误。这也是为什么我们在 main 函数的最后一般会返回0。
        当代码运行时,它会酿成一个进程。进程竣事时,main 函数的返回值现实上就是该进程的退出码。我们可以利用 echo $? 下令来检察最近一次进程退出时的退出码信息。
例如下面这个代码:
 
        代码运行竣事后,我们可以检察该进程的进程退出码。

        这时便可以确定main函数是顺利执行完毕了。 
   为什么以0表示代码执行乐成,以非0表示代码执行错误?
          由于代码执行乐成只有一种情况——乐成即为乐成——而代码执行错误大概有多种原因,例如内存空间不足、非法访问、栈溢出等。为了更好地识别错误原因,我们可以利用差别的非0退出码来分别表示这些错误情况。如许,通过查抄退出码的差别值,我们可以更具体地了解程序执行失败的原因
          C语言当中的strerror函数可以通过错误码,获取该错误码在C语言当中对应的错误信息: 
 
        运行代码后我们就可以看到各个错误码所对应的错误信息: 

        现实上Linux中的ls、pwd等下令都是可执行程序,利用这些下令后我们也可以检察其对应的退出码。
        可以看到,这些下令乐成执行后,其退出码也是0。 

        但是下令执行错误后,其退出码就是非0的数字,该数字具体代表某一错误信息。 

   注意:退出码通常都有对应的字符串含义,用于资助用户确认执行失败的原因。然而,这些退出码的具体含义是人为规定的,在差别的情况中,相同的退出码大概具有差别的字符串含义。 
    进程正常退出

        return退出

        在 main 函数中利用 return 语句来退出进程是我们常用的方法。如许做不仅可以竣事程序的执行,还可以将退出码返回给操作系统,以指示程序的执行状态。
        exit函数

        利用 exit 函数退出进程也是一种常用的方法。与 return 差别,exit 函数可以在程序中的任何位置调用,并在退出进程之前执行一系列紧张操作:

  • 执行用户通过 atexit 或 on_exit 界说的清算函数,这些函数用于释放资源或举行其他清算工作。
  • 关闭全部打开的文件流,并将全部缓存的数据写入到相应的文件,确保数据完整性。
  • 调用 _exit 函数终止进程,这一步调会立即竣事进程,而不再执行进一步的清算操作。
例如,以下代码中exit终止进程前会将缓冲区当中的数据输出。


        _exit函数

        _exit 函数通常不作为退出进程的常用方法。虽然 _exit 函数也可以在程序的任何位置调用以退出进程,但它会立即终止进程,而不会在退出之前执行任何清算工作。这意味着 _exit 函数不会执行清算函数、关闭打开的文件流或写入缓存的数据,因此其作用是直接终止进程。
例如,以下代码中利用_exit终止进程,则缓冲区当中的数据将不会被输出。


  return、exit和_exit之间的区别与联系

   区别:
          1、只有在 main 函数中的 return 语句才能有用地退出进程。在子函数中的 return 语句仅会返回到调用它的函数,而不会退出整个进程。相比之下,exit 函数和 _exit 函数可以在代码中的任何位置被调用,以退出进程
          2、利用 exit 函数退出进程时,它会执行以下操作:
  

  • 执行用户界说的清算函数(通过 atexit 或 on_exit 注册的)。
  • 冲刷(flush)全部打开的流,确保缓存数据被写入。
  • 关闭全部打开的文件流。
  • 然后再终止进程。
          3、利用 _exit 函数退出进程时,它会立即终止进程不会执行任何清算操作,如不冲刷缓冲区、不关闭流等。
  

    联系:
          1、执行 return num 在 main 函数中等同于执行 exit(num)。当 main 函数执行完毕时,它的返回值会被用作 exit 函数的参数,从而调用 exit(num) 来退出进程。
          2、利用 exit 函数退出进程时,它会执行以下步调:
  

  • 执行用户界说的清算函数(通过 atexit 或 on_exit 注册的)。
  • 冲刷缓冲区,将全部缓存的数据写入相应的文件。
  • 关闭全部打开的流,确保资源被正确释放。
  • 然后,调用 _exit 函数来现实终止进程
    进程异常退出

        情况一:向进程发送信号导致进程异常退出
        例如,在进程运行过程中,假如利用 kill -9 下令向进程发送信号,或者按下 Ctrl+C,大概会导致进程异常退出。这些信号会立即终止进程,且进程的退出通常不会执行清算操作。
        情况二:代码错误导致进程运行时异常退出
        例如,代码中存在野指针问题,或者出现除以零的情况,大概会使进程在运行时异常退出。这种情况下,程序大概会由于未处理的异常或错误而瓦解,导致进程的非正常终止。
进程等待

  进程等待的须要性



  • 当子进程退出后,假如父进程不读取子进程的退出信息,子进程会酿成僵尸进程,这会导致内存泄漏。僵尸进程是已经完成执行但其退出状态尚未被父进程读取的进程
  • 一旦进程酿成僵尸进程,即使利用 kill -9 下令也无法将其杀死,由于僵尸进程现实上已经死亡,不再执行任何操作。因此,无法对已经死去的进程举行进一步的操作
  • 对于一个进程来说,最关心的就是其父进程,由于父进程须要知道子进程完成使命的状态。
  • 父进程须要通过等待子进程的方式来接纳子进程的资源,并获取子进程的退出信息。这可以通过系统调用如 wait 或 waitpid 来实现,确保子进程的退出状态被正确处理,从而避免资源泄漏和僵尸进程的产生。
  获取子进程status

        在进程等待操作中,wait 和 waitpid 函数都有一个 status 参数,该参数是一个输出型参数,由操作系统填充,用于提供子进程的退出状态信息。


  • 假如将 status 参数传递为 NULL,表示父进程不关心子进程的退出状态信息
  • 假如提供了 status 参数,操作系统将通过该参数将子进程的退出信息反馈给父进程
        虽然 status 是一个整型变量,但不能简单地将其当作整型来看待。status 的差别比特位代表差别的信息。具体来说,我们只研究 status 的低16位,这些位的细节如下:

在 status 的低16比特位中:
        1、高8位(第8到15位):表示进程的退出状态,即退出码。可以利用宏 WEXITSTATUS(status) 来提取这个退出码。
        2、低8位(第0到7位)


  • 低7位:表示终止信号。假如进程是由于信号终止的,这些比特位会指示终止信号的编号。可以利用宏 WTERMSIG(status) 来提取。
  • 第8位:表示是否生成了 core dump。假如这个标志被设置,表示进程终止时生成了 core dump 文件。可以利用宏 WCOREDUMP(status) 来查抄。
         我们可以通过一系列位操作来提取 status 中的进程退出码和退出信号。 
  1. exitCode = (status >> 8) & 0xFF; //退出码
  2. exitSignal = status & 0x7F;      //退出信号
复制代码
         对于此,系统当中提供了两个宏来获取退出码和退出信号。


  • WIFEXITED(status):用于检察进程是否是正常退出,本质是查抄是否收到信号。
  • WEXITSTATUS(status):用于获取进程的退出码。
  1. exitNormal = WIFEXITED(status);  //是否正常退出
  2. exitCode = WEXITSTATUS(status);  //获取退出码
复制代码
  注意:当一个进程非正常退出时,即该进程是由于信号终止的,那么该进程的退出码通常没有意义
    进程等待的方法

  wait方法

   函数原型: pid_t wait(int* status);
  功能: 用于等待任意子进程的竣事。
  返回值: 假如调用乐成,返回被等待进程的进程ID (pid),假如失败,则返回 -1。
  参数: status 是一个输出参数,用于接收子进程的退出状态。假如不关心退出状态,可以将其设置为 NULL。
  例如,创建子进程后,父进程可利用wait函数一直等待子进程,直到子进程退出后读取子进程的退出信息。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/wait.h>
  5. #include <sys/types.h>
  6. int main()
  7. {
  8.         pid_t id = fork();
  9.         if(id == 0){
  10.                 //child
  11.                 int count = 10;
  12.                 while(count--)
  13.         {
  14.                         printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());
  15.                         sleep(1);
  16.                 }
  17.                 exit(0);
  18.         }
  19.         //father
  20.         int status = 0;
  21.         pid_t ret = wait(&status);
  22.         if(ret > 0)
  23.     {
  24.                 //wait success
  25.                 printf("wait child success...\n");
  26.                 if(WIFEXITED(status))
  27.         {
  28.                         //exit normal
  29.                         printf("exit code:%d\n", WEXITSTATUS(status));
  30.                 }
  31.         }
  32.         sleep(3);
  33.         return 0;
  34. }
复制代码
        我们可以利用以下监控脚本对进程举行实时监控: 
  1. while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;echo "######################";sleep 1;done
复制代码
        这时我们可以看到,当子进程退出后,父进程读取了子进程的退出信息,子进程也就不会酿成僵尸进程了。 

  waitpid方法

   函数原型: pid_t waitpid(pid_t pid, int* status, int options);
  功能: 用于等待特定子进程的竣事或任意子进程的竣事。
  返回值:
  

  • 假如调用乐成,返回被等待进程的进程ID (pid)。
  • 假如设置了 WNOHANG 选项,并且没有任何子进程已退出,则返回0。
  • 假如调用过程中出现错误,则返回 -1,此时 errno 将被设置为相应的错误码以指示问题地点。
  参数:
  

  • pid:指定要等待的子进程ID。假如设置为 -1,则表示等待任意子进程。
  • status:输出参数,用于接收子进程的退出状态。假如不须要获取退出状态,可以将其设置为 NULL。
  • options:设置为 WNOHANG 时,假如没有子进程竣事,waitpid 会立即返回0而不举行等待。假如子进程已竣事,则返回该子进程的进程ID。
  例如,创建子进程后,父进程可利用waitpid函数一直等待子进程(此时将waitpid的第三个参数设置为0),直到子进程退出后读取子进程的退出信息。 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/wait.h>
  5. #include <sys/types.h>
  6. int main()
  7. {
  8.         pid_t id = fork();
  9.         if (id == 0)
  10.         {
  11.                 //child         
  12.                 int count = 10;
  13.                 while (count--)
  14.         {
  15.                         printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());
  16.                         sleep(1);
  17.                 }
  18.                 exit(0);
  19.         }
  20.         //father           
  21.         int status = 0;
  22.         pid_t ret = waitpid(id, &status, 0);
  23.         if (ret >= 0)
  24.     {
  25.                 //wait success                    
  26.                 printf("wait child success...\n");
  27.                 if (WIFEXITED(status))
  28.         {
  29.                         //exit normal                                 
  30.                         printf("exit code:%d\n", WEXITSTATUS(status));
  31.                 }
  32.                 else
  33.         {
  34.                         //signal killed                              
  35.                         printf("killed by siganl %d\n", status & 0x7F);
  36.                 }
  37.         }
  38.         sleep(3);
  39.         return 0;
  40. }
复制代码
        在父进程运行过程中,我们可以实验利用kill -9下令将子进程杀死,这时父进程也能等待子进程乐成。 

   注意: 被信号杀死而退出的进程,其退出码将没有意义。 
    多进程创建以及等待的代码模型

        我们还可以利用一种技术,通过创建多个子进程并让父进程依次等待每个子进程的退出,这种方法被称为多进程创建与等待模型
例如,以下代码中同时创建了10个子进程,同时将子进程的pid放入到ids数组当中,并将这10个子进程退出时的退出码设置为该子进程pid在数组ids中的下标,之后父进程再利用waitpid函数指定等待这10个子进程。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. int main()
  7. {
  8.         pid_t ids[10];
  9.         for (int i = 0; i < 10; i++)
  10.     {
  11.                 pid_t id = fork();
  12.                 if (id == 0)
  13.         {
  14.                         //child
  15.                         printf("child process created successfully...PID:%d\n", getpid());
  16.                         sleep(3);
  17.                         exit(i); //将子进程的退出码设置为该子进程PID在数组ids中的下标
  18.                 }
  19.                 //father
  20.                 ids[i] = id;
  21.         }
  22.         for (int i = 0; i < 10; i++)
  23.     {
  24.                 int status = 0;
  25.                 pid_t ret = waitpid(ids[i], &status, 0);
  26.                 if (ret >= 0)
  27.         {
  28.                         //wait child success
  29.                         printf("wiat child success..PID:%d\n", ids[i]);
  30.                         if (WIFEXITED(status))
  31.             {
  32.                                 //exit normal
  33.                                 printf("exit code:%d\n", WEXITSTATUS(status));
  34.                         }
  35.                         else
  36.             {
  37.                                 //signal killed
  38.                                 printf("killed by signal %d\n", status & 0x7F);
  39.                         }
  40.                 }
  41.         }
  42.         return 0;
  43. }
复制代码
        运行代码,这时我们便可以看到父进程同时创建多个子进程,当子进程退出后,父进程再依次读取这些子进程的退出信息。

  基于非阻塞接口的轮询检测方案

        在上面的例子中,当子进程尚未退出时,父进程会一直处于等待状态,这种等待方式被称为阻塞等待。在这种模式下,父进程无法举行其他操作,直到子进程退出。
        为了避免这种情况,我们可以接纳非阻塞等待的方式。如许,父进程在子进程未退出时,可以继续执行自己的使命,而在子进程退出后,再去获取子进程的退出信息。如许可以提高父进程的效率,使其在等待期间可以或许举行其他操作。
   我们可以通过,向waitpid函数的第三个参数potions传入WNOHANG,如许一来,等待的子进程若是没有竣事,那么waitpid函数将直接返回0,不予以等待。而等待的子进程若是正常竣事,则返回该子进程的pid。
  例如,父进程可以隔一段时间调用一次waitpid函数,若是等待的子进程尚未退出,则父进程可以先去做一些其他事,过一段时间再调用waitpid函数读取子进程的退出信息。 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. int main()
  7. {
  8.         pid_t id = fork();
  9.         if (id == 0)
  10.     {
  11.                 //child
  12.                 int count = 3;
  13.                 while (count--)
  14.         {
  15.                         printf("child do something...PID:%d, PPID:%d\n", getpid(), getppid());
  16.                         sleep(3);
  17.                 }
  18.                 exit(0);
  19.         }
  20.         //father
  21.         while (1)
  22.     {
  23.                 int status = 0;
  24.                 pid_t ret = waitpid(id, &status, WNOHANG);
  25.                 if (ret > 0)
  26.         {
  27.                         printf("wait child success...\n");
  28.                         printf("exit code:%d\n", WEXITSTATUS(status));
  29.                         break;
  30.                 }
  31.                 else if (ret == 0)
  32.         {
  33.                         printf("father do other things...\n");
  34.                         sleep(1);
  35.                 }
  36.                 else
  37.         {
  38.                         printf("waitpid error...\n");
  39.                         break;
  40.                 }
  41.         }
  42.         return 0;
  43. }
复制代码
        运行效果就是,父进程每隔一段时间就去检察子进程是否退出,若未退出,则父进程先去忙自己的事情,过一段时间再来检察,直到子进程退出后读取子进程的退出信息。

进程程序替换

  替换原理

        利用 fork 创建子进程后,子进程会执行与父进程相同的程序(虽然大概执行差别的代码路径)。假如我们盼望子进程执行一个完全差别的程序,通常须要调用 exec 函数。
        当进程调用 exec 函数时,进程的用户空间代码和数据会被新程序完全替换,接着从新程序的入口点开始执行。这意味着原程序的代码和数据将被新程序的代码和数据取代。

   当举行进程程序替换时,有没有创建新的进程? 
          在进程程序替换之后,虽然进程的用户空间代码和数据被新程序替换了,但进程的进程控制块(PCB)进程地址空间以及页表等数据结构保持不变。这意味着,进程并没有被重新创建,而是原有的进程在物理内存中的数据和代码被新的程序所取代。因此,替换程序前后的进程标识符(PID)保持不变。
    子进程举行进程程序替换后,会影响父进程的代码和数据吗? 
          当子进程刚被创建时,它与父进程共享代码和数据。然而,假如子进程须要举行进程程序替换,这通常意味着子进程会对其代码和数据举行修改。这时,系统会执行写时拷贝(Copy-On-Write)操作,将父子进程共享的代码和数据举行分离。如许,子进程举行程序替换时,原有的父进程的代码和数据不会受到影响,两者的代码和数据也就分离开来。
    替换函数

        替换函数有六种以exec开头的函数,它们统称为exec函数:
  1. 1、int execl(const char *path, const char *arg, ...);
复制代码
        第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要怎样执行这个程序,并以NULL结尾。 
例如,要执行的是ls程序。
  1. execl("/usr/bin/ls", "ls", "-a", "-i", "-l", NULL);
复制代码
  1. 2、int execlp(const char *file, const char *arg, ...);
复制代码
        第一个参数是要执行程序的名字,第二个参数是可变参数列表,表示你要怎样执行这个程序,并以NULL结尾。 
例如,要执行的是ls程序。
  1. execlp("ls", "ls", "-a", "-i", "-l", NULL);
复制代码
  1. 3、int execle(const char *path, const char *arg, ..., char *const envp[]);
复制代码
        第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要怎样执行这个程序,并以NULL结尾,第三个参数是你自己设置的情况变量。
例如,你设置了MYVAL情况变量,在mycmd程序内部就可以利用该情况变量。
  1. char* myenvp[] = { "MYVAL=2024", NULL };
  2. execle("./mycmd", "mycmd", NULL, myenvp);
复制代码
4、int execv(const char *path, char *const argv[]);
        第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要怎样执行这个程序,数组以NULL结尾。
例如,要执行的是ls程序。
  1. char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
  2. execv("/usr/bin/ls", myargv);
复制代码
  1. 5、int execvp(const char *file, char *const argv[]);
复制代码
         第一个参数是要执行程序的名字,第二个参数是一个指针数组,数组当中的内容表示你要怎样执行这个程序,数组以NULL结尾。
例如,要执行的是ls程序。
  1. char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
  2. execvp("ls", myargv);
复制代码
  1. 6、int execve(const char *path, char *const argv[], char *const envp[]);
复制代码
         第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要怎样执行这个程序,数组以NULL结尾,第三个参数是你自己设置的情况变量。
例如,你设置了MYVAL情况变量,在mycmd程序内部就可以利用该情况变量。
  1. char* myargv[] = { "mycmd", NULL };
  2. char* myenvp[] = { "MYVAL=2024", NULL };
  3. execve("./mycmd", myargv, myenvp);
复制代码
  函数表明



  • 假如这些函数调用乐成,它们将加载指定的程序,并从新程序的启动代码开始执行,此时不会再返回到原来的程序中。
  • 假如调用失败,函数会返回 -1。换句话说,只要 exec 系列函数返回值不为 -1,就表示调用失败。
  命名明确

        exec 系列函数的函数名都以 exec 开头,其后缀的含义如下:


  • l (list): 参数以列表情势传递,逐一列出。
  • v (vector): 参数以数组情势传递。
  • p (path): 能自动搜索情况变量 PATH 来查找程序。
  • e (env): 可以传入自界说的情况变量。
  函数名
  参数格式
  是否带路径
  是否利用当前情况变量
  execl
  列表
  否
  是
  execlp
  列表
  是
  是
  execle
  列表
  否
  否,需自己组装情况变量
  execv
  数组
  否
  是
  execvp
  数组
  是
  是
  execve
  数组
  否
  否,需自己组装情况变量
          现实上,execve 是唯一真正的系统调用,其它五个 exec 系列函数终极都是通过 execve 实现的。因此,execve 在 man 手册的第2节,而其他五个函数则在第3节。这意味着,其他五个 exec 系列函数现实上是对系统调用 execve 的封装,以适应差别用户的调用需求。 


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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

曂沅仴駦

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

标签云

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