Linux进程控制

1.进程终止

进程终止的本质是释放系统资源,就是释放进程申请的相关内核数据和对应的数据和代码。

进程退出场景

代码运行完毕,结果正确

代码运行完毕,结果错误

代码异常终止

进程常见退出办法

正常终止:

1.从main返回

2.调用exit

3._exit

异常退出:

ctrl+c 信号终止

退出码

退出码(退出状态)可以告诉我们最后一次执行的命令的状态。在命令结束后,可以知道命令是成功完成还是以错误结束的,基本思想是,程序返回退出代码0表示执行成功,没有问题。代码1或0以外的任何代码都视为不成功。

退出码0表示命令执行无误,这是完成命令的理想状态

退出码1可以解释为不被允许的操作,例如没有sudo情况下使用apt或者yum;例如除以0等操作也会返回错误代码1。

130(SIGINT或^C)和143(SIGTERM)等终止信号是非常典型的,它们属于128+n信号,其中n代表终止码

可以通过strerror函数获取退出码对应的描述

指令显示退出码

echo $?

_exit函数

#include<unistd.h>

void _exit(int status);

参数:status定义了进程的终止状态,父进程通过wait获取该值

注意:虽然status是int,但是仅有低八位可以被父进程所用。所以_exit(-1)时。在终端执行 echo $?时返回值是255.

exit函数

#include<unistd.h>

void exit(int status);

exit函数里面是调用了_exit,但exit函数还做了其它事情:

1.执行用户通过atexit或on_exit定义的清理函数

2.关闭所有打开的流,所有的缓存数据均被写入

3.调用_exit

_exit函数是系统层面的,在任何地方调用这个函数都会结束整个进程,而exit在子进程调用不会影响父进程,会把退出码返回给父进程

代码示例:

复制代码
int main()
{
printf("hello");
exit(0);
}
运⾏结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
int main()
{
printf("hello");
_exit(0);
}
运⾏结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#

return退出

return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值做exit的参数。

进程等待必要性

子进程退出,父进程如果不理会,就可能造成僵尸进程问题,进而造成内存泄漏

另外,进程一旦变成僵尸状态,就刀枪不如了,kill -9也无能为力了,无法杀死一个已经死去的进程。

最后,父进程派给子进程的任务完成的如何,是需要知道的。子进程运行完成,结果对还是不对,或者是否正常退出。

父进程通过等待的方式,回收子进程资源,获取子进程退出的信息

进程等待的方法

wait方法

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int* status);

返回值:

成功就返回被等待进程pid,失败返回-1

参数:

输出型参数,获取子进程退出状态,不关心可以设置为NULL

waitpid方法

#include<sys/types.h>

#include<sys/wait.h>

1

pid_t waitpid(pid_t pid,int* status,int options);

返回值:

当正常返回的时候waitpid返回收集到的子进程的进程ID;

如果设置了选项WNOHANG,而调用中发现没有已经退出可收集,则返回0;

如果调用中出错,则返回-1,这时error会被设置成相应的值以指示错误所在

参数:

pid:

pid=-1,等待任意一个子进程,与wait等效

pid>0,等待其进程ID与pid相等的子进程

status:输出型参数

WIFEXITED(status):若为正常终止子进程返回的状态,则为真(查看进程是否正常退出)

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

option:默认为0,表示阻塞等待

WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予等待。若正常结束,则返回子进程的ID。

如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。

如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。

如果子进程不存在,则立刻出错返回。

如果子进程结束的话,父进程会阻塞直到子进程结束才继续执行父进程代码

获取子进程status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。

如果传递NULL,表示不关心子进程的退出信息状态。

操作系统会根据该参数,将子进程的退出信息反馈给父进程

status不能简单的当作整型来看待,可以当作位图来看待,具体细节下图(为status低16位)

若进程正常结束则最低的七位为0。

对于32位的然后得到退出状态的8位,先右移八位然后&0xFF,这样只有最低八位是有效的,其余制成0了。

进程之间是独立的,父进程是如何得到子进程的退出码?因为waitpid是系统的,系统是可以得到进程的信息的,然后再给父进程,父进程就得到了子进程的信息

代码示例:

复制代码
  1 #include<unistd.h>
  2 #include<sys/wait.h>
  3 #include<stdio.h>
  4 #include<stdlib.h>
  5 #include<string.h>
  6 #include<errno.h>
  7 
  8 int main()
  9 {
 10         pid_t pid;
 11         if((pid=fork())==-1)
 12         {
 13                 perror("fork fail:");
 14                 exit(1);
 15         }
 16         if(pid == 0)
 17         {
 18                 sleep(2);
 19                 exit(10);
 20         }
 21         else
 22         {
 23                 int st;
 24                 int ret=wait(&st);
 25                 if(ret>0&&(st&0x7F)==0)
 26                 {
 27                         printf("child exit code:%d\n",(st>>8)&0xFF);
 28                 }
 29                 else if(ret>0)
 30                 {
 31                         printf("sig code:%d\n",st&0x7F);
 32                 }
 33         }
 34 
 35 
 36 }

阻塞与非阻塞等待

进程的阻塞等待方式:

一直等子进程结束,不干别的

复制代码
  1 #include<unistd.h>
  2 //#include<vector>
  3 #include<sys/wait.h>
  4 #include<stdio.h>
  5 #include<stdlib.h>
  6 
  7 
  8 int main()
  9 {
 10         pid_t pid;
 11         pid= fork();
 12         if(pid ==0)
 13         {
 14                 printf("child is run,pid is :%d\n",getpid());
 15                 sleep(5);
 16                 exit(257);
 17         }
 18         else
 19         {
 20                 int status =0;
 21                 pid_t ret=waitpid(-1,&status,0);//阻塞式等待
 22                 printf("waiting\n");
 23                 if(WIFEXITED(status)&&ret==pid)
 24                 {
 25                         printf("success,child return code is:%d\n",WEXITSTATUS(status));
 26 
 27                 }
 28                 else
 29                 {
 30                         printf("wait fail");
 31                         return 1;
 32                 }
 33         }
 34         return 0;
 35 
 36 
 37 }

非阻塞状态

这个状态父进程可以不用只等待子进程,可以边等边干别的事情

复制代码
#include <sys/wait.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <errno.h>
  5 #include<stdio.h>
  6 #include<unistd.h>
  7 //#include<vector>
  8 
  9 typedef void (*handler_t)(); // 函数指针类型
 10 std::vector<handler_t> handlers; // 函数指针数组
 11 void fun_one() {
 12         printf("这是⼀个临时任务1\n");
 13 }
 14 void fun_two() {
 15         printf("这是⼀个临时任务2\n");
 16 }
 17 void Load() {
 18         handlers.push_back(fun_one);
 19         handlers.push_back(fun_two);
 20 }
 21 void handler() {
 22         if (handlers.empty())
 23                 Load();
 24         for (auto iter : handlers)
 25                 iter();
 26 }
 27 int main() {
 28         pid_t pid;
 29         pid = fork();
 30         if (pid < 0) {
 31                 printf("%s fork error\n", __FUNCTION__);
 32                 return 1;
 33         }
 34          else if (pid == 0) { // child
 35                 printf("child is run, pid is : %d\n", getpid());
 36                 sleep(5);
 37                 exit(1);
 38                 }
 39         else {
 40                 int status = 0;
 41                 pid_t ret = 0;
 42                 do {
 43                         ret = waitpid(-1, &status, WNOHANG); // ⾮阻塞式等待
 44                         if (ret == 0) {
 45                                 printf("child is running\n");
 46                         }
 47                         handler();
 48                 } while (ret == 0);
 49                 if (WIFEXITED(status) && ret == pid) {
 50                         printf("wait child 5s success, child return code is :%d.\n",
 51                         WEXITSTATUS(status));
 52}
 53                 else {
 54                         printf("wait child failed, return.\n");
 55                         return 1;
 56                 }
 57         }
 58         return 0;
 59 }
相关推荐
AlfredZhao4 小时前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户97183563346610 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪11 小时前
linux 拷贝文件或目录到指定的位置
linux
大树881 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠1 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质1 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush41 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5201 天前
Linux 11 动态监控指令top
linux
小宇宙Zz1 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工1 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信