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 }
相关推荐
大帅哥_12 分钟前
Linux的几个基本指令
linux·服务器
布Coder16 分钟前
Flowable 工作流API应用与数据库对应展示
java·服务器·前端
davenian29 分钟前
< OS 有关 > 阿里云:轻量应用服务器 的使用 安装 Tailscale 后DNS 出错, 修复并替换 apt 数据源
linux·服务器·ubuntu·阿里云·tailscale
hgdlip1 小时前
异地IP属地代理业务解析:如何改变IP属地
服务器·网络·tcp/ip
千航@abc2 小时前
vim文本编辑器
linux·运维·编辑器·vim
千航@abc2 小时前
vim文本编辑器三种模式的转换关系
linux·运维·编辑器·vim
爱吃喵的鲤鱼2 小时前
Linux——多线程的控制
linux·运维·服务器
吹个泡泡(c++后端开发)3 小时前
linux之进程信号(信号保存 & 信号处理)
linux·信号处理
流星白龙3 小时前
【Linux】16.Linux进程概念(5)
android·linux·运维
灰勒塔德3 小时前
Linux-----线程同步(条件变量)
linux·运维·服务器·c语言