当前位置: 首页 > news >正文

进程终止 等待 替换

文章目录

  • 一.进程的终止
    • 进程终止实在做什么?
    • 进程终止的3种情况
      • 自定义退出码
    • 如何终止进程?
  • 二.进程等待
    • 为什么要进行进程等待?
    • 进程如何等待?
      • wait
      • waitpid
    • 阻塞等待 && 非阻塞等待
  • 三.进程的程序替换
    • 先看代码 && 现象
      • execl
      • 原理
      • 多进程替换
    • 使用所有的替换方法,并认识函数参数的含义
      • execv
      • execvp
      • execlp
    • 用自己的程序替换
      • execvpe

一.进程的终止

进程的创建:内核的相关管理数据结构(task_struct + mm_struct + 页表) + 代码和数据
那么进程创建的时候,先有数据结构还是代码和数据?
例子:高考完被学校录取,学校就已经有你的档案(虚拟地址空间),报道的时候你才真正的到学校(物理内存)
所以创建进程先构建数据结构,后写入代码和数据
在这里插入图片描述

进程终止实在做什么?

释放曾经的代码和数据所占的空间
释放内核数据结构

进程终止的3种情况

问题:main函数的为什么要有返回值呢?

#include <stdio.h>
#include <unistd.h>int main()
{printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid());return 0;
}
#include <stdio.h>
#include <unistd.h>int main()
{printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid());return 100;
}

两段代码能正常运行
在这里插入图片描述
区别就在与echo $?的值不一样
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述运行上面的第二段代码,为什么第一个 $?是100,第二个 $?是0呢?
因为 echo也是进程
在这里插入图片描述
退出码:0表示成功 !0表示失败
在这里插入图片描述
查看错误码的函数strerror(报错信息) (注意:错误码不是退出码)
在这里插入图片描述
查看错误码编号的代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{for(int errcode = 0; errcode <= 255; errcode++){printf("%d:%s\n",errcode,strerror(errcode));}printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid());return 100;
}

在这里插入图片描述
随便写个命令,发现报错码是1,正好与上面的报错信息相对
在这里插入图片描述
问题:父进程bash为什么要得到子进程的退出码呢?
答:要知道子进程的退出情况(成功、失败、失败的原因是什么),为用户负责

自定义退出码

引子:

#include <stdio.h>int Div(int x,int y)
{if( 0 == y ){return -1;}elsereturn x / y;
}int main()
{int result = Div(10,0);printf("result:%d\n",result);return 0;
}

运行之后,输出是-1。
不知道是因为除0的原因导致结果为-1还是相除本身结果是-1
在这里插入图片描述
说明光靠打印结果(数字),是看不清涵义。
所以自定义一下:

#include <stdio.h>//自定义枚举常量
enum
{Success = 0,Div_Zero,Mod_Zero,
};int exit_code = Success;int Div(int x,int y)
{if( 0 == y ){exit_code = Div_Zero;return -1;}elsereturn x / y;
}int main()
{int result = Div(10,0);printf("result:%d\n",result);return exit_code;
}

这样就知道是因为什么退出了,退出码为1,说明除零了
在这里插入图片描述
我们不适合面对数字,更适合直观的语言,所以再写一个接口

#include <stdio.h>//自定义枚举常量
enum
{Success = 0,Div_Zero,Mod_Zero,
};int exit_code = Success;const char* CodeToErrString(int code)
{switch(code){case Success:return "Success";case Div_Zero:return "div zero!";case Mod_Zero:return "mod zero!";default:return "unknow error!";}
}int Div(int x,int y)
{if( 0 == y ){exit_code = Div_Zero;return -1;}elsereturn x / y;
}int main()
{int result = Div(10,100);printf("result:%d[%s]\n",result,CodeToErrString(exit_code));result = Div(10,0);printf("result:%d[%s]\n",result,CodeToErrString(exit_code));return exit_code;
}

效果就很明显
在这里插入图片描述
综上所述:进程终止的2种情况
a.代码跑完,代码正确
b.代码跑完,代码不正确
由进程的退出码决定
第三种情况:程序能跑完吗?(生活中的事情一定要全做完吗?)
c.代码执行时,出现了异常,提前退出了
最经典的就是野指针问题。
最明显的例子:VS 编译运行的时候,崩溃了 ------> 操作系统发现发现你的进程做了不该做的事情,OS杀掉了进程
故事:某学生考试作弊,考了90分,你还在乎他为什么考了10分嘛。
一旦出现异常,退出码就不重要了
为什么会出现了异常?原因是?
进程出现了异常,本质是因为进程收到了OS发给进程的信号!
这里证明OS发送给进程信号:

#include <stdio.h>
#include <unistd.h>int main()
{int* p = NULL;while(1){printf("I am a  process,%d\n",getpid());sleep(1);*p = 100;}return 0;
}

在这里插入图片描述
这个进程有没有野指针不重要,重要的是因为野指针了,触发了操作系统给该进程发信号
查看进程的信号:

kill -l

发的是11号信号,SIGSEGV全称为segmentation violation,就是段错误
在这里插入图片描述
把野指针删了运行:

#include <stdio.h>
#include <unistd.h>int main()
{while(1){printf("I am a  process,%d\n",getpid());sleep(1);}return 0;
}

没有野指针,收到了信号,操作系统判断就是野指针。
在这里插入图片描述
综上所述:进程出现了异常时,我们可以看进程退出的时候,退出信号是多少,就可以判断我的进程为什么异常了
衡量一个进程退出,我们只需要两个数字:退出码 && 退出信号

退出码退出信号
00完全成功
!00代码运行正常,结果不对
0!0进程出异常了,退出码对不对无所谓
!0!0进程出异常了,退出码对不对无所谓

这两个数字一定要让父进程知道
但子进程如何传给父进程信息呢?
子进程PCB有sig_code(退出数字)和exit_code(退出信号)。
在这里插入图片描述
源码:
在这里插入图片描述

如何终止进程?

a. main函数return,表示进程终止
b. 代码调用exit函数
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{while(1){printf("I am a  process,%d\n",getpid());sleep(1);exit(123);}return 0;
}

运行之后退出码为123
在这里插入图片描述
如果把exit放入到其他函数当中,退出码依旧是exit中的值

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int ADD()
{exit(13);
}int main()
{while(1){printf("I am a  process,%d\n",getpid());sleep(1);ADD();}return 0;
}

在这里插入图片描述
c. _exit --> system call
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{while(1){printf("I am a  process,%d\n",getpid());sleep(1);_exit(29);}return 0;
}

退出码和代码一样是29
在这里插入图片描述
把他放入函数调用内部

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int Function()
{exit(29);
}int main()
{while(1){printf("I am a  process,%d\n",getpid());sleep(1);Function();}return 0;
}

退出码依旧是29
在这里插入图片描述
目前功能和exit差不多
区别:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{printf("hello world");//注意这里没有\nsleep(2);exit(6);
}

printf已经执行了,存在在缓冲区,exit让我们看到了hello world,说明exit冲刷了缓冲区
在这里插入图片描述
同样的代码_exit()

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{printf("hello world");sleep(2);_exit(6);
}

hello world并没有刷出来
在这里插入图片描述
说明区别在于exit会在进程结束的时候,冲刷缓冲区,_exit不会
注意这里说的缓冲区不是操作系统内核的缓冲区
在这里插入图片描述
在这里插入图片描述

二.进程等待

任何一个子进程,在退出的情况下,一般必须要被父进程等待。
如果子进程在退出的时候,父进程不管不顾。子进程会变成为Z(僵尸状态),PCB依旧会存在,引发内存泄漏

为什么要进行进程等待?

1.父进程通过等待,解决子进程退出的僵尸问题,回收系统资源。(一定要考虑)
2.获取子进程的退出信息,知道子进程是什么原因退出的。(可选的功能)

进程如何等待?

有两个函数wait/waitpid

wait

在这里插入图片描述
演示:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d.ppid:%d\n",getpid(),getppid());pid_t id = fork();if(id == 0){//childChildRun();printf("child quit ...\n");exit(0);}//fathersleep(10);//让子进程已经退了,父进程还在sleep,就能看见子进程的僵尸状态pid_t rid = wait(NULL);//看见回收僵尸进程if(rid > 0){printf("wait success,rid:%d\n",rid);}sleep(3);printf("father quit ...\n");return 0;
}

查看进程方便:

while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep ; sleep 1; done

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以等待,可以解决子进程的僵尸问题的。
如果把父进程的sleep(10)去掉,父进程会一直等待子进程直到子进程退出。
在子进程没退的期间,父进程一直在阻塞等待。
子进程本身就是软件,父进程本质就是在等待某种软件的条件就绪,如何理解父进程堵塞等待子进程呢?
在这里插入图片描述

waitpid

在这里插入图片描述
第一个参数pid
pid=-1:等待任意一个子进程,与wait等效
pid>0 :等待其进程pid与所写的pid相等的子进程
下面这两个的作用一模一样,表示等待任何一个子进程退出,哪个退了就返回哪个子进程的pid,就不再演示了

wait(NULL);
waitpid(-1,NULL,0);

把-1改成pid,表示等待指定的子进程

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d.ppid:%d\n",getpid(),getppid());pid_t id = fork();if(id == 0){//childChildRun();printf("child quit ...\n");exit(0);}//fathersleep(10);//让子进程已经退了,父进程还在sleep,就能看见子进程的僵尸状态//pid_t rid = wait(NULL);//看见回收僵尸进程pid_t rid = waitpid(id,NULL,0);if(rid > 0){printf("wait success,rid:%d\n",rid);}sleep(3);printf("father quit ...\n");return 0;
}

waipid也能体现出回收僵尸进程
在这里插入图片描述
是有可能等待失败的,故意填一个错误的id,失败的话会返回-1

pid_t rid = waitpid(id+1,NULL,0):

在当前的系统当中,只要id不填错,基本不会等待失败
创建子进程不就是未来让子进程帮我们完成任务吗!
完成的怎样我怎么知道?
就是第二个参数 status–> 输出型参数(需要定义一块空间,把空间的地址传进来,未来在操作系统等待的时候,可以把数据通过指针让用户看到)
在这里插入图片描述
典型的输出型参数就是

int a;
scanf("%d",&a);

status 表示的子进程退出信息

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d.ppid:%d\n",getpid(),getppid());pid_t id = fork();if(id == 0){//childChildRun();printf("child quit ...\n");exit(1);}//fathersleep(7);int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){printf("wait success,rid:%d\n",rid);}else{printf("wait failed!\n");}sleep(3);printf("father quit,status:%d\n",status);return 0;
}

发现退出信息status == 256
在这里插入图片描述
退出信息:退出码 && 退出信号
问题:整俩全局变量分别表示退出码和退出信号不好吗?
答:父进程看不到子进程的数据,如果子进程修改了全局变量就会发生写实拷贝。
一个数是如何拿到两个数字?
不能把他当成一个整数
status是int类型,有32个比特位,只考虑低16位
在这里插入图片描述
用位操作符就能看到子进程的退出信息:

printf("father quit,status:%d,child quit code:%d,child quit signal:%d\n",status,(status>>8)&0xFF,status & 0x7F);

运行没问题,256 == 2^8 --> 1 0000 0000
在这里插入图片描述
为了方便测试,把exit(1)改成exit(123)
在这里插入图片描述
杀掉子进程
在这里插入图片描述
如果不想使用位操作符,推荐两个宏,这两个宏的本质就是进行位操作

WIFEXITED(status)//W:wait IF:if EXITED:退出

若为正常终止子进程的状态,则真(查看进程是否正常退出,查signal位)

WEXITSTATUS(status):

若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
应用:WIFEXITED(status) WEXITATUS(status)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process,pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d.ppid:%d\n",getpid(),getppid());pid_t id = fork();if(id == 0){//childChildRun();printf("child quit ...\n");exit(1);}//fathersleep(7);int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){if(WIFEXITED(status)){printf("child success,child exit code:%d\n",WEXITATUS(status));}else{printf("child quit unnormal!\n");}printf("wait success,rid:%d\n",rid);}else{printf("wait failed!\n");}sleep(3);return 0;
}

正常退出时:退出码为1
在这里插入图片描述
给子进程一个野指针,异常
在这里插入图片描述

阻塞等待 && 非阻塞等待

如果子进程没有退出
而父进程在进行执行waitpid等待,阻塞等待。
在这里插入图片描述
进程阻塞的时候,父进程其他事情什么都没干。
于是就有了非阻塞等待,也就是第三个参数optinos
讲个小故事理解非阻塞等待:
在这里插入图片描述

在这里插入图片描述

张三在等待李四这种过程当中,可以做其他的事情,就称为非阻塞等待。
张三同学每隔几分钟就给李四打电话,拨电话的过程就叫做函数调用。
说话的过程就叫做函数传参。
李四跟张三说我还没好,叫函数返回值。
每一次函数调用的本质就是再检测李四的状态。
在这里插入图片描述
李四那头没说好,张三就一直等待。叫做阻塞等待
打电话类似函数调用,不就绪就不返回。
waitpid检测状态的变化(也就是打电话)

pid_t waitpid(pid_t pid,int* status,int options);//options == 0 则为阻塞等待//options == WNOHANG 则为非阻塞等待

WNOHANG 本质就是一个宏。(若云服务器卡住了,一般把这种情况叫做HANG住了)
阻塞等待:
pid_t > 0 : 等待成功了,子进程退出了,并且父进程回收成功
pid_t < 0 : 等待失败了
非阻塞等待:
pid_t == 0 : 检测时成功的,只不过子进程还没退出。需要下一次进行重复等待
非阻塞等待的时候 + 循环 = 非阻塞轮询(允许父进程做一些其他的事情(比如张三在下面打王者))
应用非阻塞轮询代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);sleep(2);  //为了观察现象cnt--;}
}int main()
{printf("I am father,pid:%d.ppid:%d\n",getpid(),getppid());pid_t id = fork();if(id == 0){//childChildRun();printf("child quit ...\n");exit(66);}//fatherwhile(1){int status = 0;pid_t rid = waitpid(id,&status,WNOHANG);if(rid == 0)  //非阻塞轮询{sleep(1);  //为了观察现象printf("child is running,father check next time!\n");//DootherThing();  干一些其他事情}else if(rid > 0)  //运行成功{if(WIFEXITED(status))  //正常退出{printf("child quit sucess,child exit code : %d\n",WEXITSTATUS(status));}else  //错误退出{printf("child quit unnormal!\n");}break;  //成功了直接break}else  //等待失败{printf("waitpid failrd!\n");break;  //失败了直接break}}return 0;
}

在这里插入图片描述
这里举例父进程可以做的DootherThing:下载资源等
task.h

#pragma once#include <stdio.h>void PrintLog();
void Download();
void MysqlDataSync();

task.c

#include "task.h"void PrintLog()
{printf("begin PrintLog...\n");
}void Download()
{printf("begin Download...\n");
}void MysqlDataSync()
{printf("begin MySQLDataSync...\n");
}

myprocess.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>#include "task.h"typedef void(*func_t)();#define N 3  //三个任务
func_t tasks[N] = {NULL};void LoadTask()  //要下载的任务
{tasks[0] = PrintLog;tasks[1] = Download;tasks[2] = MysqlDataSync;
}void HandlerTask()  //处理任务
{int i = 0;for(i = 0;i < N; i++ ){tasks[i]();  //回调方式}
}void DootherThing()
{HandlerTask();
}void ChildRun()
{int cnt = 5;while(cnt){printf("I am child process,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);sleep(2);cnt--;}
}int main()
{printf("I am father,pid:%d.ppid:%d\n",getpid(),getppid());pid_t id = fork();if(id == 0){//childChildRun();printf("child quit ...\n");exit(66);}LoadTask();//fatherwhile(1){int status = 0;pid_t rid = waitpid(id,&status,WNOHANG);if(rid == 0)  //非阻塞轮询{sleep(1);printf("child is running,father check next time!\n");DootherThing();}else if(rid > 0)  //运行成功{if(WIFEXITED(status))  //正常退出{printf("child quit sucess,child exit code : %d\n",WEXITSTATUS(status));}else  //错误退出{printf("child quit unnormal!\n");}break;  //成功了直接break}else  //等待失败{printf("waitpid failrd!\n");break;  //失败了直接break}}return 0;
}

在这里插入图片描述

三.进程的程序替换

关于程序替换的函数一共有7个
在这里插入图片描述
在这里插入图片描述

先看代码 && 现象

execl

在这里插入图片描述
先看现象:

#include <stdio.h>
#include <unistd.h>int main()
{printf("testtexec ... begin!\n");execl("/usr/bin/ls","ls","-l","-a",NULL);  //可变参数不用,写NULLprintf("testtexec ... end!\n");return 0;
}

发现输出的是ls命令,ls本身就是C语言写的,用exec*函数就是执行起来新的程序(进程)
在这里插入图片描述

原理

进程 = 内核数据结构 + 代码和数据
用ls代码和数据覆盖testexec的物理内存中的数据和代码
在这里插入图片描述
进程替换的本质:用新进程的代码和数据覆盖老进程的代码和数据。
问题:在替换的时候有没有创建新的进程?
答:没有,执行新程序是拿老程序的壳子
在这里插入图片描述
回到代码:为什么前面的printf输出了,后面的printf没有输出?
在这里插入图片描述
在这里插入图片描述
execl函数的返回值可以不关心了
只要替换成功,就不会向后继续运行
只要继续运行了,一定就是替换失败了
让其失败一次:

#include <stdio.h>
#include <unistd.h>int main()
{printf("testtexec ... begin!\n");execl("/usr/bin/lslslslss","ls","-l","-a",NULL);printf("testtexec ... end!\n");return 0;
}

在这里插入图片描述

多进程替换

想要进程替换,还不想影响程序本身,可以fork创建子进程,让子进程去替换,父进程wait等待即可
先看现象:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("testtexec ... begin!\n");pid_t id = fork();if(id == 0){sleep(2);//childexecl("/usr/bin/ls","ls","-l","-a",NULL);exit(1);  //如果替换失败,则退出}//  fatherint status = 0;pid_t rid = waitpid(id ,&status,0);if(rid > 0){printf("father wait success,child exit coed:%d\n",WEXITSTATUS(status));}printf("testtexec ... end!\n");return 0;
}

子进程执行ls,父进程等待成功,退出码为0
在这里插入图片描述
创建子进程,让子进程完成任务:
1.让子进程执行父进程代码的一部分
2.让子进程执行一个全新的程序
平常创建子进程进行修改的时候,只有数据被更改
替换是连着数据和代码一起更改,发生写实拷贝
在这里插入图片描述

使用所有的替换方法,并认识函数参数的含义

在这里插入图片描述

execv

把命令一起打包传参
在这里插入图片描述
代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("testtexec ... begin!\n");pid_t id = fork();if(id == 0){sleep(2);char* const argv[] = {(char*)"ls",  //编译器检查严格,强转类型避免Warning(char*)"-l",(char*)"-a",(char*)"--color",NULL};//child//execl("/usr/bin/ls","ls","-l","-a",NULL);execv("/usr/bin/ls",argv);exit(1);  //如果替换失败,则退出}//  fatherint status = 0;pid_t rid = waitpid(id ,&status,0);if(rid > 0){printf("father wait success,child exit coed:%d\n",WEXITSTATUS(status));}printf("testtexec ... end!\n");return 0;
}

在这里插入图片描述

execvp

p表示:用户可以不传要执行的文件路径(但要传文件名),直接告诉exec*,我要执行谁即可
系统会自动在环境变量PATH中进行查找
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("testtexec ... begin!\n");pid_t id = fork();if(id == 0){sleep(2);char* const argv[] = {(char*)"ls",(char*)"-l",(char*)"-a",(char*)"--color",NULL};//child//execl("/usr/bin/ls","ls","-l","-a",NULL);//execv("/usr/bin/ls",argv);execvp("ls",argv);exit(1);  //如果替换失败,则退出}//  fatherint status = 0;pid_t rid = waitpid(id ,&status,0);if(rid > 0){printf("father wait success,child exit coed:%d\n",WEXITSTATUS(status));}printf("testtexec ... end!\n");return 0;
}

execlp

在这里插入图片描述

execlp("ls","ls","-l","-a",NULL);
execlp("top","top",NULL);

用自己的程序替换

上面的程序替换,我们替换的都是系统命令,可不可以替换我们自己写的程序?
先用C语言替换C++
mypragma.cc

#include <iostream>using namespace std;int main()
{cout << "hello C++,I am a C++ pragma!" << endl;cout << "hello C++,I am a C++ pragma!" << endl;cout << "hello C++,I am a C++ pragma!" << endl;return 0;
}

编译后形成的可执行程序为mypragma
在这里插入图片描述
用testexec替换myprogma程序

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("testtexec ... begin!\n");pid_t id = fork();if(id == 0){sleep(2);execl("./mypragma","mypragma",NULL);  //已经找到,所以可以直接写mypragma(./mypragma也行)exit(1);  //如果替换失败,则退出}//  fatherint status = 0;pid_t rid = waitpid(id ,&status,0);if(rid > 0){printf("father wait success,child exit coed:%d\n",WEXITSTATUS(status));}printf("testtexec ... end!\n");return 0;
}

在这里插入图片描述
并且改一下代码,验证进程并没有被替换
mypragma.cc

#include <iostream>
#include <unistd.h>using namespace std;int main()
{cout << "hello C++,I am a C++ pragma!" << getpid() << endl;cout << "hello C++,I am a C++ pragma!" << getpid() << endl;cout << "hello C++,I am a C++ pragma!" << getpid() << endl;return 0;
}

testexec.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("testtexec ... begin!\n");pid_t id = fork();if(id == 0){printf("child pid:%d\n",getpid());execl("./mypragma","mypragma",NULL);  //已经找到,所以可以直接写mypragma(./mypragma也行)exit(1);  //如果替换失败,则退出}//  fatherint status = 0;pid_t rid = waitpid(id ,&status,0);if(rid > 0){printf("father wait success,child exit coed:%d\n",WEXITSTATUS(status));}printf("testtexec ... end!\n");return 0;
}

发现进程的pid一样
在这里插入图片描述
不仅仅是C语言替换C++。Python,JAVA,Shell脚本都可以
任何脚本文件都有解释器,解释器都是由C/C++写的,解释器相当于可执行程序。在Linux下跑都会变成进程,只要是进程就可以被替换
比如Shell与Python:

execl("/usr/bin/bash","bash","test.sh",NULL);
execl("/usr/bin/python3","python3","test.py",NULL);

execvpe

e:environment:环境变量;envp不传参时是NULL
在这里插入图片描述
argv 是不是很像main函数的参数 ; envp 是环境变量表
mypragma.cc

#include <iostream>
#include <unistd.h>using namespace std;int main(int argc,char* argv[],char* env[])
{int i = 0;for(;argv[i];i++)  //打印命令行参数{printf("argv[%d] : %s\n",i,argv[i]);}printf("-------------------------------------\n");for(int i = 0;env[i];i++)  //打印环境变量{printf("env[%d] : %s\n",i,env[i]);}printf("-------------------------------------\n");cout << "hello C++,I am a C++ pragma!" << getpid() << endl;cout << "hello C++,I am a C++ pragma!" << getpid() << endl;cout << "hello C++,I am a C++ pragma!" << getpid() << endl;return 0;
}

testexec.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{printf("testtexec ... begin!\n");pid_t id = fork();if(id == 0){char* const argv[] = {(char*)"mypragma",NULL};char* const envp[] = {(char*)"HAHA=111111",(char*)"hehe=222222",NULL};printf("child pid:%d\n",getpid());execvpe("./mypragma",argv,envp);exit(1);  //如果替换失败,则退出}//  fatherint status = 0;pid_t rid = waitpid(id ,&status,0);if(rid > 0){printf("father wait success,child exit coed:%d\n",WEXITSTATUS(status));}printf("testtexec ... end!\n");return 0;
}

环境变量也传给子进程mypragma.cc了
在这里插入图片描述
当然也可以直接传选项:

char* const argv[] = {(char*)"mypragma",(char*)"-a",(char*)"-b",NULL};

选项也就有了
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 2024了,Neo4j能显示节点图片吗?
  • [深度学习] 时间序列分析工具TSLiB库使用指北
  • 【二叉树】OJ题目
  • Android 架构模式之 MVVM
  • AWS CodeCommit 停服,欢迎大家使用极狐GitLab!
  • Scrapy入门学习
  • Windows IPv6漏洞CVE-2024-38063
  • spring boot 集成es使用
  • (十三)Flink SQL
  • 海南省政协主席李荣灿调研宇乐乐影业
  • 如何使用 Java 将 JSON 数据转换为 YAML 文件
  • python并发与并行(四) ———— 用queue来协调多个线程之间的工作进度
  • 【Qt】基于VTK9.1+VS2019+Qt5.15.2的点云可视化程序开发
  • 【C++ Primer Plus习题】6.9
  • Elasticsearch(面试篇)
  • 5、React组件事件详解
  • cookie和session
  • input实现文字超出省略号功能
  • java2019面试题北京
  • Java多线程(4):使用线程池执行定时任务
  • nodejs:开发并发布一个nodejs包
  • npx命令介绍
  • Python利用正则抓取网页内容保存到本地
  • Rancher如何对接Ceph-RBD块存储
  • Solarized Scheme
  • SQLServer插入数据
  • 分布式任务队列Celery
  • 批量截取pdf文件
  • 如何设计一个比特币钱包服务
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 做一名精致的JavaScripter 01:JavaScript简介
  • Java总结 - String - 这篇请使劲喷我
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​queue --- 一个同步的队列类​
  • #WEB前端(HTML属性)
  • #图像处理
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $.each()与$(selector).each()
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (11)MSP430F5529 定时器B
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (ibm)Java 语言的 XPath API
  • (SERIES12)DM性能优化
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (三十)Flask之wtforms库【剖析源码上篇】
  • (十三)Flask之特殊装饰器详解
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .axf 转化 .bin文件 的方法
  • .NET Core 2.1路线图
  • .NET gRPC 和RESTful简单对比
  • .NET/C# 使用反射注册事件
  • @Autowired注解的实现原理
  • [20150321]索引空块的问题.txt