微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Linux操作系统之进程控制

进程创建

我们创建一个进程的时候是使用系统调用接口:

#include <unistd.h>
pid_t fork(void); 
// 返回值,子进程中返回0,父进程返回子进程的id,出错返回-1

当进程调用fork()之后,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块儿和内核数据结构给子进程
  • 将父进程部分数据结构拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

我们在之前就已经说过了,一开始父进程和子进程指向的其实都是同一份空间。

 此时,我们把页表项100这个部分改成非只读,这个时候我们就会发生缺页中断

缺页中断:进程往一个不可写的内存写入了,操作系统就会把进程中断,例如上面的图片,我本来是只读的,我把它改成可读可写了,这个时候映射关系就会出现问题了,操作系统这个时候检测到了之后就会把进程停止,发生缺页中断,然后修改页表和对应关系,这个时候我的子进程被中断了,不知道发生了什么,这是分离现象修改完之后写时拷贝,物理内存分开。

 因此,进程创建从时间和空间上的成本都非常高。

进程终止

进程退出的时候会发生三种情况:

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

我们知道当程序结束的时候,我们可以使用return,但是有的同学可能知道,exit(EXIT_SUCCESS)也可以完成进程退出操作,那么这两者有什么不同呢?

exit终止进程,强制终止进程,不需要进行进程的后序收尾工作,例如刷新缓冲区,而return可以进行后序的收尾工作。

 进程退出的时候,系统层面少了一个进程,free PCB,free mm_struct, free页表和各种映射关系,代码和数据申请的空间也要全部去掉。

进程控制

wait方法

函数原型:pid_t wait(int* status);

作用:等待任意子进程。

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

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

waitpid方法

函数原型:pid_t waitpid(pid_t pid, int* status, int options);

作用:等待指定子进程或任意子进程。

返回值:
1、等待成功返回被等待进程的pid。
2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。

参数:
1、pid:待等待子进程的pid,若设置为-1,则等待任意子进程。
2、status:输出型参数,获取子进程的退出状态,不关心可设置为NULL。
3、options:当设置为WNOHANG时,若等待的子进程没有结束,则waitpid函数直接返回0,不予以等待。若正常结束,则返回该子进程的pid。

 我们的父进程fork之后可以得到子进程,子进程存在的目的是帮助父进程完成某种任务,于是我们的父进程在程序的结尾需要wait子进程,以保证子进程一定在父进程之后退出,为什么要这么做呢?

理由其实很简单:

  1. 通过获取子进程退出的信息,能够得知子进程执行结果。假如说我和我爸去买酱油,我爸必须要等我买完了看我到底买完了没。
  2. 可以保证时序问题,子进程先退出,父进程后退出
  3. 进程退出的时候会先进入僵尸状态,会造成内存泄漏的问题。需要通过父进程wait,释放该子进程占用的资源!!!

获取子进程status

  • waitwaitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status16比特

  这个core dump一般是0

我们通过一系列位操作,就可以根据status得到进程的退出码和退出信号。

exitCode = (status >> 8) & 0xFF; //退出码(1111 1111)
exitSignal = status & 0x7F;      //退出信号(0111 1111)



对于此,系统当中提供了两个宏来获取退出码和退出信号。

  • WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号。
  • WEXITSTATUS(status):用于获取进程的退出码。
exitnormal = WIFEXITED(status);  //是否正常退出
exitCode = WEXITSTATUS(status);  //获取退出


需要注意的是,当一个进程非正常退出时,说明该进程是被信号所杀,那么该进程的退出码也就没有意义了。

阻塞等待和非阻塞等待

一个现实生活中的例子。假如说我要找张三借复习资料,但是张三说,我现在在看书,等我30分钟。那么我这个时候给张三打了一个电话,并且没有挂断电话,等张三下楼之前我都不会挂断电话,也就是说我电话功能全部被封死了,一直在通话中。这个叫阻塞等待

但是假如我每过两分钟给张三打一个电话的话,每次打电话都确认他是否可以下来了,那么这个时候我的电话就有被闲置的时间让我完成其他的任务,这个叫基于非阻塞等待的轮询方案

不管是阻塞还是非阻塞,都是等待的一种方式。谁等??等谁??等什么??

等子进程。子进程退出一个条件or事件。

阻塞了是不是意味了父进程不被执行了或者不被调度执行了呢??

父进程如果在阻塞状态等子进程的话,那么它就是在纯等,什么都不会干。父进程本来是R(运行状态的)就会被成为S(等待状态),被加入到等待队列里面去。等子进程搞完之后发现父进程在等待,那么就再把等待队列里面的父进程放过来变成R。

阻塞的本质就是把进程的PCB被放入了等待队列,并将进程的状态改为S状态。返回的本质,进程的PCB从等待队列拿到R队列,从而被cpu调度。

非阻塞等待的代码

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
     pid_t pid;
 
     pid = fork();
     if(pid < 0){
         printf("%s fork error\n",__FUNCTION__);
     return 1;
     } else if( pid == 0 ){ //child
         printf("child is run, pid is : %d\n",getpid());
         sleep(5);
         exit(1);
     } else{
     int status = 0;
     pid_t ret = 0;
     do
     {
         ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
     if( ret == 0 ){
         printf("child is running\n");
     }
     sleep(1);
     }while(ret == 0);
 
     if( WIFEXITED(status) && ret == pid ){
         printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
     }else{
         printf("wait child Failed, return.\n");
         return 1;
     }
     }
     return 0;
}

我们可以看到有一个WNHOANG,加上这个,我们就可以实现非阻塞的等待了。

进程替换

用fork创建子进程后,子进程执行的是和父进程相同的程序(但有可能执行不同的代码分支),若想让子进程执行另一个程序,往往需要调用一种exec函数

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,并从新程序的启动例程开始执行。

当进行进程程序替换时,有没有创建新的进程?

进程程序替换之后,该进程对应的PCB、进程地址空间以及页表等数据结构都没有发生改变,只是进程在物理内存当中的数据和代码发生了改变,所以并没有创建新的进程,而且进程程序替换前后该进程的pid并没有改变。

子进程进行进程程序替换后,会影响父进程的代码和数据吗?

子进程刚被创建时,与父进程共享代码和数据,但当子进程需要进行进程程序替换时,也就意味着子进程需要对其数据和代码进行写入操作,这时便需要将父子进程共享的代码和数据进行写时拷贝,此后父子进程的代码和数据也就分离了,因此子进程进行程序替换后不会影响父进程的代码和数据。

也就是说进程替换就是用一个老的进程的壳子去执行一个新的代码,没用创建新的进程就执行了代码

它的本质是:把指定的进程代码 + 数据,加载进特定进程的上下文中去。
exec函数实际上起到了加载器的作用,通过程序替换的方式来把磁盘上的东西加载到内存。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐