开发者社区> 游客5fwd2mps6myf6> 正文

Linux系统编程——控制进程

简介: Linux系统编程——控制进程
+关注继续查看

文章目录

进程创建

fork

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

  • 分配新的内存块核心数据结构给子进程
    (进程PCB进程地址空间页表构建映射关系)
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统列表当中
  • fork返回后,开始调度器调度写时拷贝

写时拷贝

image

页表中需要有2^32个地址需要映射,如果页表每一部分6字节

则一共需要24GB


缺页中断:父子当中突然改变了数据,OS就发生了中断,但是之前那个空间是共享的,OS就开辟了一个空间,把老的空间给拷过来,OS重新修改了页表,映射关系,把只读选项给去掉了,保持进程的独立性

有了写时拷贝,就能保证父子进程数据的独立性


进程终止

把进程开辟的东西都释放掉

进程退出场景

  1. 代码运行完毕,结果是正确的
  2. 代码运行完毕,结果是错误的
  3. 代码异常中止
return 0;

为什么main函数要return 0,意义在哪里?进程的退出码,衡量代码跑完了,是对还是不对,

return 0代表success

return 100

echo $?查看退出码

image

退出码会被父进程继承

echo $?

会输出最近一次进程退出时的退出码

image

而第二次输出0

是echo命令的退出码

错误的退出码

每个退出码都有其对应的错误原因

  1 #include<stdio.h>
  2 #include<string.h>
  3 int main()
  4 {
  5   //printf("hello\n");
  6   int i=0;
  7   for(i=0;i<100;i++)
  8   {
  9    printf("%d: %s \n",i,strerror(i));//返回错误信息,;里面对应的i就是返回的错误码,返回的信息就是对应的错误信息                                                                                 
 10   }
 11 
 12   return 123;
 13 }

image错误码与错误描述


代码异常终止

运行终止的退出码,我们并不关系


进程常见退出方法

1.main函数return,代表进程退出!!

而非main函数


2.exit(退出码)进程退出程序

在任意地方使用都是让进程退出

参数是退出码


都可以达到进程退出的目的


3._exit(退出码)

强制终止进程,不要进行进程的后续收尾工作,如刷新缓冲区,


#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int fun()
{
  printf("fun test");
  return 1;
}
int main()
{
  //fun();
  //printf("hello\n");
  //int a=10;
  //a/=0;//跑到一半异常终止
  //在vs中一般叫程序崩溃
  //

 // int i=0;
 // for(i=0;i<140;i++)
 // {
 //   printf("%d: %s \n",i,strerror(i));//返回错误信息,;里面对应的i就是返回的错误码,返回的信息就是对应的错误信息
 // }

  printf("hello");//此时数据被暂时保存在输入缓冲区中
  sleep(3);
   _exit(12);
  //  exit(EXIT_SUCCESS);
 //exit或return 本身就是会要求系统进行缓冲区刷新 
  //return 123;
}

用户级缓冲区image进程退出在操作系统层面做了什么?

系统场面上少了一个进程,free PCB,free 页表,mm_struct,页表和各种映射关系,代码+数据申请的空间也要被释放掉


进程等待

进程等待是什么?

fork():创建子进程(为了帮助父进程某种任务),父进程

让我们的父进程fork之后,需要通过wait/waitpid来等待子进程退出

为什么要让父进程等待呢

通过获取子进程退出的信息,能够得知子进程执行结果

可以保证,时序问题,子进程先退出,父进程后退出

进程退出的时候,会先进入僵尸状态,会造成内存泄漏的问题,需要通过父进程wait,来释放该子进程占用的空间(子进程的僵尸资源)

进程进入了僵尸进程,kill -9就没有办法杀掉该进程

进程等待的方法

wait

作用是等待任意一个子进程

头文件


1 #include <stdio.h>

2 #include<sys/types.h>

3 #include<sys/wait.h>

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

#include <stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
  pid_t id=fork();
  if(id==0)
  {
    //child
    int cnt=5;
    while(cnt){
      printf("child[%d] is running : cnt is :%d \n",getpid() ,cnt);
      cnt--;
      sleep(1);
      //子进程会持续5秒之后就exit,但是父进程;立刻就退出了
      //子进程就会变成一个孤儿进程,被系统领养
    }
    exit(0);//我们执行完子进程之后直接退出,不执行父进程的代码
    //进程退出
  }
  sleep(10);//先让父进程等待10秒
  //前5秒是正常运行的
  //后5秒子进程处于z状态
  printf("father wait begin\n");
  //因为父进程和子进程是同时执行的
  //parent
  //为了不让子进程变成一个孤儿进程,父进程必须在那里等待
  pid_t ret=wait(NULL);//成功的话就返回其
  if(ret>0)
    printf("father wait %d\n ",ret);
  else 
    printf("father wait fail\n");
  //到这里僵尸状态就没了
  sleep(10);
  //回收完毕之后父进程继续活上10秒
  return 0;
}

image


一开始前5秒都共同运行着,5秒之后,子进程退出,进入z状态,因为父进程还在sleep,没有去wait,再过5秒,父进程开始执行wait,wait成功,僵尸进程就没了,再过了10秒钟,父进程就退出了


wait完全可以回收僵尸进程

waitpid

成功了就会返回其等待的子进程的pid,错了就会返回-1

 #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *wstatus);

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

waitpid就是一个系统调用,

status就是一个输出形参数

最终一定要让父进程,通过status,得到子进程执行的结果

正常退出代码

image

异常终止代码

image

imageimagebash是命令行启动的所有进程的父进程|

bash一定是通过wait 方式得到子进程的退出结果,所以我们能看到echo$?

查看到子进程的退出码


status:

WIFEXITED(status):若进程正常退出,就为真

WEXITSTATUS(status):若WIFEXITED非0,就可以提取子进程的退出码

(就不要进行麻烦的位操作)

imageoption

0为默认行为,阻塞等待

掉wait的时候,父进程啥也不干,就在哪里等子进程,等待子进程退出,

WNOHANG:设置等待方式为非阻塞等待

阻塞等待

啥也不干,就在那里静静的等待,就等子进程退出才会返回


阻塞的本质

就是将进程的PCB被放入到等待队列当中,并将进程的状态改为S状态,

返回的本质,进程的PCB 从等待队列中拿到R 队列,从而被CPU 调度,

image


非阻塞等待

检测子进程的运行状态,等待的过程中,可能需要多次检测:

基于非阻塞等待的轮询方案,


调用一个接口,CPU立马返回,CPU不断重复的调度父进程,就是重复调度waitpid的过程


无论是阻塞还是非阻塞,都是等待的一种方式,

谁等(父进程)

等(子进程)

等待子进程退出(条件/事件)


我们看到吗某些运用或者OS本身,长时间不动,是应用或程序hang住了

WNOHANG:非阻塞


返回值结果,子进程改变就没有退出,

子进程退出,waitpid(调用成功or失败),

#include <stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
  pid_t id=fork();
  if(id==0)
  {
    //child
    int cnt=5;
    while(cnt){
      printf("child[%d] is running : cnt is :%d \n",getpid() ,cnt);
      cnt--;
      sleep(1);
      //子进程会持续5秒之后就exit,但是父进程;立刻就退出了
      //子进程就会变成一个孤儿进程,被系统领养
    }
    exit(0);//我们执行完子进程之后直接退出,不执行父进程的代码
  }
  int status=0;
 
  //pid_t ret=waitpid(id,&status,0);//阻塞等待
  while(1)//非阻塞的轮询方案
  {
  pid_t ret=waitpid(id,&status,WNOHANG);//非阻塞等待
  if(ret==0){
   
    //检测子进程没有退出,但是waitpid等待是成功的,需要父进程继续进行等
    printf("do father thing\n");//得知还没好,就可以做一下自己的事情
    
  }
  else if(ret>0)
  {

    //子进程退出了,waitpid也成功了,获取到了对应的结果
    printf("exit code: %d\n,",WEXITSTATUS(status));
    break;
  }
  else 
  {
    //ret<0
    //等待失败
    perror("waitpid");
    break;
  }
  sleep(1);
  }
  return 0;

}

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Linux系统配置(进程和计划任务)
查看和控制进程、查看进程、控制进程、任务计划管理、一次性任务、周期性任务
37 0
《UNIX网络编程 卷2:进程间通信(第2版)》——1.8 书中IPC例子索引表
生产者-消费者:一个或多个线程或进程(生产者)把数据放到一个共享缓冲区中,另有一个或多个线程或进程(消费者)对该共享缓冲区中的数据进行操作。序列号持续增1:一个或多个线程或进程给一个共享的序列号持续增1。该序列号有时在一个共享文件中,有时在共享内存区中。
1132 0
Linux下C编程,子进程创建函数fork() 执行解析
最近在看进程间的通信,看到了fork()函数,虽然以前用过,这次经过思考加深了理解。现总结如下: 1.函数本身   (1)头文件   #include  #include   (2)函数原型   pid_t fork( void);  (pid_t 是一个宏定义,其实质是int 被定义在#include中)  返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1   (3)函数说明   一个现有进程可以调用fork函数创建一个新进程。
767 0
《UNIX网络编程 卷2:进程间通信(第2版)》——1.9 小结
各种类型IPC的持续性可以是随进程持续的、随内核持续的或随文件系统持续的,这取决于IPC对象存在时间的长短。在为给定的应用选择所用的IPC类型时,我们必须清楚相应IPC对象的持续性。
1346 0
Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介
原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理子系统、网络子系统、虚拟文件系统在这个阶段进行分析的,但是为了让大家对内核有个整体的把握,今天还是简单的介绍一下剩余的几个子系统,我们对这几个子系统的分析,只要了解其作用和部分内容即可,不必深究,等我们写上几个驱动,到时候按照驱动再来分析这几个子系统我们就清晰多了。
1049 0
[推荐]ORACLE PL/SQL编程详解之三:PL/SQL流程控制语句(不给规则,不成方圆)
原文:[推荐]ORACLE PL/SQL编程详解之三:PL/SQL流程控制语句(不给规则,不成方圆) [推荐]ORACLE PL/SQL编程详解之三: PL/SQL流程控制语句(不给规则,不成方圆) ——通过知识共享树立个人品牌。
1001 0
Linux下C编程,进程通信之无名管道通信
最近在看进程间的通信,下面说说管道通信之无名管道。 1.概述   管道是Linux中很重要的一种通信方式,他是把一个程序的输出直接连接到另一个程序的输入,并且管道具有队列的特性。如Linux命令,“ps -ef | grep root”。
925 0
Linux下apache服务器安装,sqlite安装,apache启动,关闭,重启,编写cig程序进行测试,浏览器访问cig程序
 1安装ubuntu 14.04 64位操作系统 2安装apache和sqllite以及依赖的sqllite开发库 3配置目录 /etc/apache2 4 html页面目录 var/www/ 5 cgi-bin目录 /usr/lib/cgi-bin 6日志文件:/var/log/apache2 7站点配
2102 0
《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》——2.14 进程0由0特权级翻转到3特权级,成为真正的进程
本节书摘来自华章计算机《Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理》一书中的第2章,第2.14节,作者:新设计团队著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1211 0
Linux系统编程-(pthread)线程通信(信号量)
信号量的运用环境与互斥锁一样,但是信号量比互斥锁增加灵活,互斥锁只有两个状态(开锁和解锁),而信号量本质上是一个计数器,它内部有一个变量计数信号值,可以保护一个资源可以同时被1个或者2个或者3个线程同时使用,如果信号量的值只是设置1(状态只有0和1),那么和互斥锁就是一样的功能。
58 0
84
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载