进程退出:
return(main函数中return 才叫进程终止) exit (库函数),_exit系统调用接口,二者是上下级的调用关系
//man 2 exit,2手册是系统调用接口
区别 main 函数中的return 刷新缓冲区释放资源
exit 刷新缓冲区释放资源
而_exit释放资源
printf(“nihao shijie”);
6 sleep(3);
7 _exit(0); 没有打印结果
8 //exit(0); 打印了你好世界
9 //return 0; 打印 了你好世界
查看退出的返回值
exit(99)/_exit(99)
echo
?enyongjie@localhostlinux] echo $?
99
三者返回值 return 程序正常退出结果符合预期
exit程序正常退出结果不符合预期
而_exit异常退出
echo $? 查看进程退出返回值
perror 打印系统调用接口错误原因
strerror(error)将系统调用错误编码号解释为字符串原因
返回值用一个自己保存一个字节八位全1为255如果返回值大于255比如是25
6则变成全1进位低八位全部为0所以返回值随机显示
将错误编号解释为字符串的错误原因
头文件string
strerror()
for(;i<256;i++){
8 printf(“error:[%s]
”,strerror(i));
9 }
如下显示
error:[Success]
error:[Operation not permitted]
error:[No such file or directory]
error:[No such process]
error:[Interrupted system call]
error:[Input/output error]
error:[No such device or address]
error:[Argument list too long]
perror直接打印系统错误原因
头文件为errorn.h
perror(“fork error”);
printf(“fork error :%s
” strerror(errno);
二者等价
进程等待:获取子进程退出码,允许系统回收释放子进程资源避免僵尸进程
进程等待等待:子 进程退出wait—2号手册系统调用接口
为什么?避免产生僵尸进程
因为父进程不知道子进程什么时候退出,因此只能在子进程创建之后调用wait,进行进程等待因为调用wait就是一直在等待子进程退出
int main(){
6 int pid=fork();
7 if(pid<0){
8 perror(“for error”);
9 exit(-1);
10 }else if (pid==0){
11 sleep(5);
printf(“wan
”);
12 exit(0);
13 }
14 int status;
15 wait(&status);//等待直到任意一个子进程退出
16 while(1){
17 printf(“playing
”);
18 sleep(2);
19 }
20 return 0;
21 }
while [ true ] ;do ps aux |grep wait;sleep 0.5;done
while [ true ] ;do ps aux |grep wait;sleep 0.5;done
chenyon+ 8051 0.0 0.0 4208 348 pts/0 S+ 09:42 0:00 ./wait
chenyon+ 8052 0.0 0.0 4208 88 pts/0 S+ 09:42 0:00 ./wait
chenyon+ 8054 0.0 0.0 112724 984 pts/1 S+ 09:43 0:00 grep
监控查看子进状态可以看到子进程没有了父进程才开始执行/1
wait只是等待任意一个子进程退出
pid_t wait(int *status);
status:用于退出返回值
返回值:返回退出子进程的PID出错-1
pid_t waitpid(pid_t pid;int *status,int options)
22 // pid :指定子进程id
23 // -1等待任意子进程
24 // >0等待指定子进程
25 // status 用于获取返回值
26 // option 选项参数
27 // WNOHANG
while(waitpid(pid,&status,WNOHANG)==0){
33 // perror(“waitpid error”);
34 printf(“no exit----smoking–
”);
35 sleep(1);
36 }
37 while(1){
38 printf(“打麻将
”);//playing
39 sleep(2);
40 }
子进程没有退出抽烟也就是可以干其他事然后过一会在过去看子进程有没有退出子进程退出了自己可以玩/打麻将了
./wait
no exit----smoking–
no exit----smoking–
no exit----smoking–
no exit----smoking–
no exit----smoking–
wan
playing
playing
playing
wait接口是一个阻塞函数
阻塞:为了完成某个功能发起调用,如果当前不具备完成条件一直等待直到 子进程退出
非阻塞:为了完成某个功能发起调用,如果当前不具备完成条件,直接报错返回
int pid=fork();
7 if(pid<0){
8 perror(“for error”);
9 exit(-1);
10 }else if (pid==0){
11 sleep(5);
12 printf(“wan
”);
13 exit(255);
14 // exit(0);
15 }
16 int status;
while(waitpid(pid,&status,WNOHANG)==0){
35 // perror(“waitpid error”);
36 printf(“no exit----smoking–
”);
37 sleep(1);
38 }
39 printf(“子进程退出:%d
”,status);
40 while(1){
41 printf(“playing
”);
42 sleep(2);
43 }
44 return 0;
本来退出值应该是255但是现在是65280为什么
[chenyongjie@localhost linux]$ ./wait
no exit----smoking–
no exit----smoking–
no exit----smoking–
no exit----smoking–
no exit----smoking–
wan
子进程退出:65280
playing
waitpid(pid,&status,WNOHANG)==0
status获取的返回值不是255(exit(255))是什么数据?
子进程退出返回值fan只拿一个字节保存但是此处接收为&status,为int型是四个字节所以此处waitpid返回字节有可能不在低八位,返回值为255为一个字节但是获取时候用4个字节获取,如果四个字节没有其他数据放到第八位里数字打印就是255但是不是255,所以返回值不在低八位,这四个字节里面不仅仅包含返回值还包含了其他信息并且返回值不在低八位四个字节的分布四个字节总共用 了低16位高16位没有用咋低16位中255保存在高八位里,剩下的低八位中又进行划分,低八位的最高那位(高1位)存放的是一个core dump标志低七位存放的是异常状态(异常退出原因),异常退出只是针对这个进程但是其异常退出时在操作系统的可控范围之内,这个异常退出原因就是操作系统通知的信号值
segmentation falut(core dumped)
段错误就是操作系统检测到进程发生内存访问错误这个时候通过信号来通知进程说发生内存访问错误,进程知道后自己退出(程序的奔溃),程序奔溃是操作系统可控制范围之内
core dumped核心转储文件程序奔溃了就把程序的运行数据和信息保存下来
在低八位的最高位中的core dump标志就是要不要把核心转储文件保存下来,用1位进行了标记
core dumped 保存异常退出时程序的运行信息
那么如何获取255也就是退出返回值只需要将(status)>>8&&0xff(右移8位然后与就可以了)
如果程序异常终止,低7位保存异常信号值值>否则低七位为0;通过低七位是否为0判断程序是否异常退出
if(!status&&0x7f)去掉高1位留下低七位,低7位为0则正常退出
if(!(status&0x7f)){
41 printf(“chlid exit:%d”,(status)>>8&0xff);
42 //获取退出返回值
43 }
封装好的宏为WIFEXITED判断是否正常退出正常退出返回真
WEXITSTATUS获取为真的返回值
if(WIFEXITED(status)){
45 printf(“chlid exit:%d”,WEXITSTATUS(status));
46 }
WIFEXITED是否因为异常信号值终止,如果是则返回信号值
if(WIFEXITED(status)){
49 printf(“chlid signal:%d”,WTERMSIG(status));
50 }
程序替换:
运行程序就是把一个程序加载到内存中然后创建一个PCB,然后指向内存中代码的位置 以及数据的位置进程就是运行的该程序,程序替换就是加载另一份程序加载另一份程序到内存更改pcb内存中内存指针所指向的位置,指向下一个进程则pcb就是运行的另一个进程,pcb并没有变,当前pcb运行的程序变了原先的数据也就没用了,通过虚拟地址空间找到代码数据了位置,替换成另一个程序,则代码段和运行数据都不一样了Pcb和虚拟地址空间还是一样的东西只是里面内容变了程序替换是不和创建子进程一样从创建子进程那个位置开始运行,程序替换是替换之后从新从main函数开始,相当于程序从新开始运行
为什么替换:让子进程完成其他功能
如何替换:(接口)
Exec函数族
man execl可看3号手册为库函数
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 execvpe(const char *file, char *const argv[],
char *const envp[]);
调用的是execve这个函数为2号手册
execl
eg硬盘两个程序一个miain 一个ls 当运行main加载到内存但是这时候进行程序替换替换成ls程序,得告诉exec替换的程序路径path而file是只需要名称就可以execl和execlp区别以及v和vp的区别传参是否需要路径
execlechar * const envp[];这个参数就是环境变量,父进程通过子进程传递环境变量就是通过这个参数传递,将自己的环境变量全部传递给子进程
ls -a -l
execl("/bin/ls",“ls”,"-a","-l",NULL)//NULL作为参数压栈的结尾
第一个参数给程序的路径
程序的运行参数从第0个参数是它自己
然后接下来的参数才是我们真正想要赋予的参数最终参数完毕以空结尾
[chenyongjie@localhost linux]$ ./exec
nihaoshijie
总用量 132
-rw-rw-r–. 1 chenyongjie chenyongjie 63 4月 3 10:31 a.c
-rwxrwxr-x. 1 chenyongjie chenyongjie 8584 4月 3 08:12 a.out
-rw-rw-r–. 1 chenyongjie chenyongjie 476 4月 3 08:27 child.c
-rwxrwxr-x. 1 chenyongjie chenyongjie 8536 4月 3 11:19 env
-rw-rw-r–. 1 chenyongjie chenyongjie 723 4月 3 12:21 env.c
-rwxrwxr-x. 1 chenyongjie chenyongjie 8528 4月 4 12:24 exec
-rw-rw-r–. 1 chenyongjie chenyongjie 148 4月 4 12:23 exec.c
printf(“nihaoshijie
”);
7 execl("/bin/ls",“ls”,"-l",NULL);
8 return 0;
在你好世界之后程序进行替换了
char *env[32];
10 env[0]=“MYENV=10000”;
11 env[1]=NULL;
12 execle("./env",“ls”,"-l",NULL,env);
13 printf(“nihao
”);//程序替换成功后面打印不出来
14 return 0;
设置环境变量
int i;
23 for(i=0;i
24 printf(“argv=[%d]=[%s]
”,i,argv[i]);//获取参数
25 }
26 extern char **environ;
27 for(i=0;environ[i]!=NULL;i++){
28 printf(“env[%d]=[%s]
”,i,environ[i]);//打印所有的环境变量
29 }
30 printf(“MYENV:[%s]
”,getenv(“MYENV”));
31 return 0;
chenyongjie@localhost linux]$ ./exec
nihaoshijie
argv=[0]=[ls]
argv=[1]=[-l]
env[0]=[MYENV=10000]
MYENV:[10000]
并没有打印全部环境变量,-l是我们给的但是只到了env[0];
如果我们给自己设置环境变量原有的环境变量就继承不下来了
自己只给自己设置了一个所以只有env[0]
所以对于execle我们设置什么环境变量才会有什么环境变量
对于execl和execlp就是对于这个execle("./env",“ls”,"-l",NULL,environ);
传了个environ把自身全部传进去了