【linux】进程控制

简介: 【linux】进程控制

进程的独立性

进程:内核相关管理数据结构(pcb+地址空间+页表)+代码+数据

当fork函数创建一个子进程时,如何保证进程具有独立性?

父进程的数据以及代码来自磁盘,而子进程的代码是共享父进程的,至于数据,如果子进程不修改数据的话,同样也是和父进程共享的,而如果子进程要修改数据,就会发生写时拷贝,保证数据的独立性,子进程的退出也不会对父进程做干扰.父进程退出也不会对子进程做干扰.

frok函数的返回值

子进程给父进程返回自己的pid,父进程给子进程返回0,这样是为了对子进程进行标识,进而进行管理.这个在之前的文章中有讲过

进程的终止

进程终止做了什么

1.释放曾经的代码和数据所占的空间。

2.释放内核的数据结构(pcb+地址空间+页表)

当时当子进程退出后,父进程还没有获取到子进程的状态时,子进程的代码和数据已经被释放,而内核中的数据结构还没有被释放。因为子进程的内核数据结构pcb中保存了子进程的退出码和退出信号,需要等待父进程获取.

进程终止的3种情况

1.代码跑完,结果正确

2.代码跑完,结果错误

3.代码执行时,出现了异常,提前退出了


下面看一段程序,这段程序代码会跑完,结果也正确

这段程序中的return 0表示什么呀?

其实这个进程相当于bash的子进程,而main函数return的是该进程的退出码,该子进程会将该退出码返回给bash,因为bash要知道子进程退出的情况,给用户提供失败的原因是什么,对用户负责.

这个退出码 可以通过指令

echo $?

0就是这个程序的退出码,一般success表示成功

strerror

针对上面的错误码的话,对计算机而言,他知道退出码0,1,2,3对应着什么,我们可以通过strerror函数查看对应的退出码对应的是什么错误

退出码:0成功

!0失败

不同的非0值一方面表示失败,另一方面表示失败的原因,而对于计算机可以将退出码和错误做对应,如果我们想知道退出码对应的错误,就可以通过strerror函数来确定,对应的退出码都有错误描述string


退出码

如果我们将退出码设置为1的话

为什么第一次获取到的退出码是1,然后使用pwd进程后,在获取退出码的时候就变成0了呢?

其实父进程bash获取到的,是最近一个子进程退出的退出码,也就是说最后的0的退出码是pwd进程执行成功给bash的退出码,需要告诉父进程,子进程把任务完成的怎么样了


退出码可以使用默认,也可以自己定义

枚举类型:0表示成功,1表示除0,2表示模0,exit_code为退出码,刚开始让他=0表示成功.

然后Div函数里面如果分母为0了,就说明除0了,就把他的退出码修改成1;

打印退出码和退出码对应的信息,这里已经打印,就不用通过bash查看退出码了


退出信号

针对进程终止的前两种情况。我们可以通过系统或者自定义的退出码来确定。而针对于第三种,程序出现异常,在vs中,编译运行的时候,就会崩溃,原因是操作系统发现你做了不该做的事情,操作系统杀死了进程,一旦出现异常,退出码就没有任何意义了

下面展示代码演示代码出现异常

当对空指针解引用并赋值100时,会出现段错误,出现野指针问题.

而段错误在信号类型属于11号信号

进程出现异常本质是因为进程收到了os发给进程的信号

所以进程的终止,我们只需要两个信息,一个是退出码,一个是退出信号,

先确定是否异常,如果不是异常,就是代码跑完了,看退出码就可以了

退出码 退出信号 程序
0 0 程序跑完,结果正确
!0 0 程序跑完,结果不正确
!0 !0 异常
0 !0 异常

进程如何终止

1.main函数中的return 表示进程终止(非main函数,return表示函数结束)

2.crrl+c (异常终止)

3.exit函数,_exit函数(正常终止)

exit函数

#include<stdlib.h>
void exit(int status);
• 1
• 2

exit函数中的参数status是退出码,exit函数不管在程序的任何一个位置,只要到了exit的话,就表示进程要终止了,而return只有在main函数里面的表示进程要结束了,其他调用函数里面return只能说明函数结束.

_exit函数也是退出进程,基本上和exit的功能一样

#include <unistd.h>
void _exit(int status);
• 1
• 2

exit函数和_exit函数的区别和联系

区别 联系
exit是库函数,而_exit是系统调用,exit会冲刷缓冲区,_exit不会冲刷缓冲器 两者都会结束进程

这里的缓冲区不是内核缓冲区,因为操作系统的内核缓冲区在系统调用底层,如果是内核缓冲区的话,_exit也会冲刷缓冲区,作为库函数exit会调用_exit

验证exit会冲刷缓冲区,而_exit不会:

helloworld会立刻打印出来是因为exit会冲刷缓冲区

helloworld没有打印出来,是因为helloworld此时在缓冲区

进程等待

概念

任何子进程,在退出的情况下,一般必须要被父进程进行等待,进程在退出的时候,如果父进程不管不顾,退出进程,子进程会出现僵尸状态,导致内存泄露.

为什么要进程等待

1.父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(必须)

2.获取子进程的退出信息,知道子进程是因为什么原因退出的(选择)

怎么实现进程等待(通过wait/waitpid)

wait方法

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int*status);

返回值:

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

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

代码演示

#include<stdio.h>
   #include<stdlib.h>
   #include <unistd.h>
   #include<sys/types.h>
   #include<sys/wait.h>
   void childrun()//5秒子进程退出
   {
   int cnt=5;
   while(cnt)
  {
  printf("i am child process,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
  sleep(1);
  cnt--;
  }
  }
  int main()
  {
  printf("i am father, pid:%d,ppid:%d\n",getpid(),getppid());
  size_t id=fork();
  if(id==0)
  {childrun();                                                                
    printf("child quit ...\n");//提示子进程退出
    exit(0);//子进程退出
  }
  sleep(10);//等待10秒是为了让查看状态看到子进程退出后处于的僵尸状态
  int  rd=wait(NULL);//rd就是等待的子进程的pid
  if(rd>0)
  {
  printf("wait success,rd:%d\n",rd);
  }
  }

rd是子进程的pid,然后子进程的资源被回收,就不会引起内存泄漏.

如果子进程没有退出,父进程其实一直在进行阻塞等待,子进程本身就是软件,父进程本质上在等待某种软件条件就绪,父进程阻塞等待子进程,就会将父进程的pcb连接到子进程的运行队列里面去,直到子进程退出,父进程会将获取子进程pcb里退出号,以及退出信号,然后父进程会回收子进程pcb资源.

waitpid方法

#include<sys/types.h>

#include<sys/wait.h>

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

返回值:

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

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

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

参数:

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

pid>0.等待其进程id与pid相等的子进程。

options设置为0

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

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

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

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

获取子进程status

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

如果传递NULL,表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):

(status>>8)&0xFF //取次低8位退出号
status&0x7F //取低7位退出状态
#include<stdio.h>
   #include<stdlib.h>
   #include <unistd.h>
   #include<sys/types.h>
   #include<sys/wait.h>
   void childrun()//5秒子进程退出
   {
   int cnt=5;
   while(cnt)
  {
  printf("i am child process,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
  sleep(1);
  cnt--;
  }
  }
  int main()
  {
  printf("i am father, pid:%d,ppid:%d\n",getpid(),getppid());
  size_t id=fork();
  if(id==0)
  {childrun();                                                                
    printf("child quit ...\n");//提示子进程退出
    exit(0);//子进程退出
  }
  sleep(10);//等待10秒是为了让查看状态看到子进程退出后处于的僵尸状态
  int  rd=waitpid(-1,NULL,0);//rd就是等待的子进程的pid
  if(rd>0)
  {
  printf("wait success,rd:%d\n",rd);
  }
  }

情况1正常退出

#include<stdio.h>
   #include<stdlib.h>
   #include <unistd.h>
   #include<sys/types.h>
   #include<sys/wait.h>
   void childrun()//5秒子进程退出
   {
   int cnt=5;
   while(cnt)
  {
  printf("i am child process,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
  sleep(1);
  cnt--;
  }
  }
  int main()
  {
  printf("i am father, pid:%d,ppid:%d\n",getpid(),getppid());
  size_t id=fork();
  if(id==0)
  {childrun();                                                                
    printf("child quit ...\n");//提示子进程退出
    exit(0);//子进程退出
  }
  sleep(10);//等待10秒是为了让查看状态看到子进程退出后处于的僵尸状态
  int status=0;
  int  rd=waitpid(id,&status,0);//rd就是等待的子进程的pid
  if(rd>0)
  {
  printf("wait success,rd:%d exit_code:%d exit_singal:%d\n",rd(status>>8)&0xFF,status&0x7f); 
  }
  }

情况2 9号信号验证

将上述代码子进程改成死循环

情况3 11号信号验证

将子进程中的循环改成cnt,然后将之前的段错误的代码加到子进程中去

目录
相关文章
|
6天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
25 1
|
2月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
42 0
|
1天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
9天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
1月前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
119 4
linux进程管理万字详解!!!
|
14天前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
25天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
64 8
|
22天前
|
网络协议 Linux 虚拟化
如何在 Linux 系统中查看进程的详细信息?
如何在 Linux 系统中查看进程的详细信息?
41 1
|
22天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
1月前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
69 4