【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函数

c 复制代码
#include<stdlib.h>
void exit(int status);

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

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

c 复制代码
#include <unistd.h>
void _exit(int status);

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

代码演示

c 复制代码
#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比特位):

c 复制代码
(status>>8)&0xFF //取次低8位退出号
c 复制代码
status&0x7F //取低7位退出状态
c 复制代码
#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正常退出

c 复制代码
#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,然后将之前的段错误的代码加到子进程中去

相关推荐
蜜獾云7 分钟前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
小屁不止是运维9 分钟前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
Hacker_Oldv13 分钟前
WPS 认证机制
运维·服务器·wps
bitcsljl22 分钟前
Linux 命令行快捷键
linux·运维·服务器
ac.char25 分钟前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu
Cachel wood44 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Youkiup1 小时前
【linux 常用命令】
linux·运维·服务器
qq_297504611 小时前
【解决】Linux更新系统内核后Nvidia-smi has failed...
linux·运维·服务器
_oP_i1 小时前
.NET Core 项目配置到 Jenkins
运维·jenkins·.netcore
weixin_437398211 小时前
Linux扩展——shell编程
linux·运维·服务器·bash