文章目录
一.进程的终止
进程的创建:内核的相关管理数据结构(task_struct + mm_struct + 页表) + 代码和数据
那么进程创建的时候,先有数据结构还是代码和数据?
例子:高考完被学校录取,学校就已经有你的档案(虚拟地址空间),报道的时候你才真正的到学校(物理内存)
所以创建进程先构建数据结构(PCB),后写入代码和数据
进程终止是在做什么?
1.释放曾经的代码和数据所占的空间
2.释放内核数据结构 --> Z(僵尸状态)会被延迟处理
进程终止的3种情况
问题:main函数的为什么要有返回值呢?
实验:两段代码差异在return的值不一样
#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 $?
的值不一样
运行上面的return 100的代码,连续两次echo $? 为什么第一个 $?是100,第二个 $?是0呢?
因为 echo也是进程
退出码:0表示成功 !0表示失败
非零的退出码会被OS解释成一个错误码
查看错误码的函数strerror(报错信息)
查看错误码对于错误解释的代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
for(int errcode = 0; errcode <= 255; errcode++) //错误码最多就有255个,后面会解释为什么
{
printf("%d:%s\n",errcode,strerror(errcode));
}
printf("I am process,pid:%d,ppid:%d\n",getpid(),getppid());
return 100;
}
随便写个命令,发现报错码是1,正好与上面的下标为2的错误信息相对
问题:父进程bash为什么要得到子进程的退出码呢?
答:要知道子进程的退出情况(成功、失败、失败的原因是什么),为用户负责
自定义退出码
引子:
#include <stdio.h>
int Div(int x,int y)
{
if( 0 == y )
{
return -1;
}
else
return 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;
}
else
return 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;
}
else
return 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;
}
没有野指针,收到了信号,操作系统判断就是野指针。
综上所述:进程出现了异常时,我们可以看进程退出的时候,退出信号是多少,就可以判断我的进程为什么异常了
衡量一个进程退出,我们只需要两个数字:退出码 && 退出信号
退出码 | 退出信号 | 运行 |
---|---|---|
0 | 0 | 完全成功 |
!0 | 0 | 代码运行正常,结果不对 |
0 | !0 | 进程出异常了,退出码对不对无所谓 |
!0 | !0 | 进程出异常了,退出码对不对无所谓 |
这两个数字一定要让父进程知道,要让父进程知道子进程做的怎么样!
但子进程如何传给父进程信息呢?
子进程PCB有sig_code(退出数字)和exit_code(退出信号)。
源码:
如何终止进程?
a. main函数return,表示进程终止
b. 代码调用exit函数
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;
}
_exit
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差不多
exit 与 _exit的区别
区别:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
printf("hello world");//注意这里没有\n
sleep(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依旧会存在,引发内存泄漏(跟malloc后不delete性质相似)
为什么要进行进程等待?
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)
{
//child
ChildRun();
printf("child quit ...\n");
exit(0);
}
//father
sleep(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)
{
//child
ChildRun();
printf("child quit ...\n");
exit(0);
}
//father
sleep(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)
{
//child
ChildRun();
printf("child quit ...\n");
exit(1);
}
//father
sleep(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)
{
//child
ChildRun();
printf("child quit ...\n");
exit(1);
}
//father
sleep(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)
{
//child
ChildRun();
printf("child quit ...\n");
exit(66);
}
//father
while(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)
{
//child
ChildRun();
printf("child quit ...\n");
exit(66);
}
LoadTask();
//father
while(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); //最后要以NULL结尾 跟argv[]最后以NULL结尾相似
printf("testtexec ... end!\n");
return 0;
}
发现输出的是ls命令,ls本身就是C语言写的。exec*函数就是执行新的程序(进程)
原理
进程 = 内核数据结构 + 代码和数据
用ls代码和数据覆盖testexec的物理内存中的数据和代码 (覆盖的同时堆与栈也会刷新)
进程替换的本质:用新进程的代码和数据覆盖老进程的代码和数据。
问题:在替换的时候有没有创建新的进程?
答:没有,执行新程序是拿老程序的壳子 (pid不变)
回到代码:为什么前面的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);
//child
execl("/usr/bin/ls","ls","-l","-a",NULL);
exit(1); //如果替换失败,则退出
}
// father
int 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); //如果替换失败,则退出
}
// father
int 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); //如果替换失败,则退出
}
// father
int 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
同样是传文件名,与execvp是一对。
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); //如果替换失败,则退出
}
// father
int 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); //如果替换失败,则退出
}
// father
int 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与老的进程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); //如果替换失败,则退出
}
// father
int 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
};
选项也就有了
上面的函数都出自三号手册,都是C语言层做了简单的封装
系统封装只有execve
execve
有这么多函数就是为了支持不同的应用场景