IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 进程控制(退出&等待&程序替换&简单shell的实现) -> 正文阅读

[系统运维]进程控制(退出&等待&程序替换&简单shell的实现)

1.进程退出的理论

进程退出有三个场景:
①代码运行完毕,结果正确
②代码运行完毕,结果不正确
③代码异常终止

main函数的返回值实际上是进程的退出码
echo $?指令可以输出最近一次程序退出时的退出码
在这里插入图片描述

在这里插入图片描述
后面的echo 为什么输出的是0?
以为最近的一次指令是上一次的echo,所以return 返回的是0
在这里插入图片描述
退出码可以来判断程序执行的结果是正确还是不正确
我们一般用 0代表success , !0 代表failed
比如在这边错误的退出码就是2(2只是不正确情况中的一种,实际情况而定 )
在这里插入图片描述我们可以打印一下,看一下各种错误码
在这里插入图片描述
在这里插入图片描述
代码异常终止,在我们的VS中被称为程序崩溃,也就是说代码还没有跑完就终止了,在这种情况下,错误码是没有意义的了,因为是unknown的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 .进程退出的方式

有三种方式

方式一:return

main函数return, 代表进程退出,非main函数的return叫做函数返回

方式二:exit

exit在任意地方调用,都叫做终止进程,参数是退出码
在这里插入图片描述
在这里插入图片描述
数据这边本身是会被放在缓冲区的,但是exit or main里面的return 本身就会要求系统进行,缓冲区刷新!
总结:return 和 exit 除了帮我们能够退出程序以外,还可以帮助我们刷新缓冲区
在这里插入图片描述
在这里插入图片描述

方式三:_exit

_exit终止进程,强制终止进程,不要进行后续的收尾工作,比如刷新缓冲区
在这里插入图片描述在这里插入图片描述
一张图说明exit()和_exit的区别,_exit()直接终止,exit()要做以下的一系列工作
ps:不刷新缓冲区!=不释放系统资源
在这里插入图片描述

3 .进程退出,OS做了什么?

系统层面上来看:少了一个进程:free PCB, free mm_struct , free 页表和各种映射结构,代码+数据,申请的空间也要被杀掉

4.进程等待

原理

①进程wait是什么

父进程fork出来的子进程是为了帮父进程完成某种任务,父进程进行fork的时候,需要通过wait/waitpid等待子进程的退出
在这里插入图片描述

②为什么要让父进程等待

1 .通过获取子进程退出的信息,能够得知子进程执行结果
2 .可以保证:时序问题,子进程先退出,父进程再退出
3 .子进程退出的时候会进入僵尸状态,会造成内存泄漏的问题,需要通过父进程wait,释放该子进程占用的资源

③怎么进行进程等待(wait,waitpid)

在这里插入图片描述

wait
不想让子进程完成之后还执行父进程的代码,子进程可能要执行五秒,父进程早就退出了,子进程会被系统领养,变成孤儿进程;然而,我们不想这么干,我们要等待子进程
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
我们可以看到刚开始程序有两个进程,慢慢地子进程没有被父进程回收变成了僵尸进程
在这里插入图片描述
接着父进程开始等待,回收子进程
在这里插入图片描述
最后父进程也退出程序结束
说明:我们的wait是可以回收僵尸进程的

waitpid
这里第一个参数指定为id的话等待的就是id
在这里插入图片描述
在这里插入图片描述
这里第一个参数如果是-1的话,表示的是等待任意 一个 子进程,等价于刚刚的wait
在这里插入图片描述
这里的status是输出型参数
status的构成:有32个比特位,只使用低16位比特位,高16位比特位不用
在这里插入图片描述

在这里插入图片描述
父进程拿到什么status结果,一定和子进程如何退出强相关!!!
子进程退出的话题,不就是我们刚刚将的进程退出吗?

在这里插入图片描述
在这里插入图片描述
最终一定要让父进程通过status得到子进程执行的结果
执行的结果就是之前讲的三种
在这里插入图片描述
结果正确和不正确,也就是看退出码,也就是return or exit的结果
如何判断代码异常终止?本质是这个进程因为异常问题,导致自己收到了某种信号

实操

①获取子进程的退出结果(右移方式)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
再稍微修改一下,就是我们代码正确的情况
在这里插入图片描述

在这里插入图片描述
bash是命令行启动的所有进程的父进程,bash一定是通过wait方式得到子进程的退出结果,所以我们能看到echo $?能够查到子进程的退出码!

②接口方式

status:
WIEXITED(status):查看进程是否是否正常退出
WEXITSTATUS(status):若WIFEXITED非0,提取子进程退出码(查看进程退出码)
在这里插入图片描述
在这里插入图片描述

③WNOHANG

第三个参数OPTIONS:
WNOHANG:设置等待非阻塞
首先理解阻塞和非阻塞:

阻塞等待:现实生活中,等一个朋友的期间什么都没有干,没等到就不回家(不挂电话,朋友一直在电话中等)

非阻塞等待:现实生活中,不停的打电话挂电话询问,检测朋友的运行状态
可能需要多次检测:基于非阻塞状态等待的轮询方案

OS层面上看
阻塞的本质:其实是进程的PCB被放入了等待队列,并将进程的状态改为S状态
返回的本质:进程的PCB从等待队列拿到R队列,从而被CPU调度
在这里插入图片描述

无论是阻塞非阻塞,都是等待的一种。谁等?等谁?等什么?
现在这种情况是父进程在等子进程退出
在这里插入图片描述
在这里插入图片描述

5.进程的程序替换

①什么是程序替换

进程不变,仅仅替换当前进程的代码和数据的技术,叫做,进程的程序替换

·

相当于我们用了一个老的进程的壳子,执行了新的代码和数据
有没有创建新的进程?没有
在这里插入图片描述
我们发现后面打印的语句竟然被替换了
为什么后面的代码没有执行??因为程序替换,后面的代码和数据全都被替换掉了
在这里插入图片描述
在这里插入图片描述
程序替换的本质就是 把指定的代码+数据,加载进特定进程的上下文中!!
在这里插入图片描述
子进程进行代码替换,父进程间断持续打印,最后阻塞回收
在这里插入图片描述
在这里插入图片描述
但是为什么子进程在执行的时候,父进程也在执行呢???
答案是进程具有独立性
之前所说的父子进程代码共享是建立在没有进程程序替换的前提下的,
也就是说,
进程程序替换会更改代码区的代码,也要发生写时拷贝

联想前面,我们创建子进程的目的,是为了让子进程执行父进程代码的一部分!那么如果想让子进程执行一个“全新的程序”呢?
程序替换!

只要exec*的函数返回了,就一定是因为调用失败了,不然一定不会使用后续的代码的?

②为什么要执行程序替换

目的也就是让子进程去执行一个全新的程序!

③各个程序替换函数的基本使用(execl,p,v,ve,le)

使用man execl查看使用
在这里插入图片描述
先写一个测试代码
在这里插入图片描述
我们发现程序替换之后的代码end<<也没有了
也可以知道execl返回的话函数必定出错了
在这里插入图片描述
为什么出来有execl , 还有execlv , execlv这些呢????
命名理解
在这里插入图片描述

l ( list ) : 表示参数采用列表
这样一个一个传入就叫列表
在这里插入图片描述

v ( vector ) : 参数用数组
在这里插入图片描述

p ( path ): 有p自动搜索环境变量PATH
只要传file名帮我们自动寻找
在这里插入图片描述
在这里插入图片描述
vp也就是v+p的功能同理
在这里插入图片描述
在这里插入图片描述

e (env) : 表示自己维护环境变量

我们想要通过执行myexe.c然后把我们的myload.c给跑起来
在这里插入图片描述
把myexec.c全部替换成myload.c的过程

在这里插入图片描述
Makefile默认只会执行第一个依赖关系,所以如果我们想要同时编译两个文件,就需要写成下面的这种方式
想执行多个执行文件只需要在all的后面往后跟就可以了
在这里插入图片描述
我们成功地通过运行myload将myexe的内容执行了
在这里插入图片描述
在这里插入图片描述
验证execle
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
也就是说,带e的可以自定义环境变量
这样来看
execve也很简单的
适当修改代码,检验同样也是可以的
在这里插入图片描述
在这里插入图片描述
总结:以后写程序如果有其他语言写的代码,我们可以通过exec进行接口调用
这里用c++程序调用python代码举个例子
在这里插入图片描述
在这里插入图片描述

为什么会有这么多的接口?
是为了满足不同的应用场景的,但是万变不离其宗
在这里插入图片描述

④制作一个简单的shell

什么是解释器?
解释器的本质不就是给你一个命令窗口,然后不断的获取你的输入吗?

如何获得你的输入?
在这里插入图片描述
C语言中的strtok()?
在这里插入图片描述
我们的“破烂版”mini_shell

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<stdlib.h>
    4 #include<sys/wait.h>
    5 #include<string.h>
    6 
    7 #define NUM 128
    8 #define CMD_NUM 64//最多可以定义64个字符
    9 
   10 int main()
   11 {
   12   char command[NUM];
   13   for(;;)
   14   {
   15     //步骤一:打印提示符
   16     char *argv[CMD_NUM] = {NULL};//这是一个指针数组,可以用来拆开 ls -l -a 的每个选项
   17     command[0]=0;//这种方式可以做到O(1)的复杂度清空字符串                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
   18     printf("[super_zkx@myhostname minishell]#");
   19     fflush(stdout);
   20 
   21     //步骤二:获取字符串
   22     fgets(command,NUM,stdin);
   23     //输入的时候会多一个回车
   24     command[strlen(command)-1] ='\0';//"ls\n\0" "\n"算的是一个字符,strlen算出来是3 - 1 = 2  \n对应下标刚好是2 
   25 
   26     //步骤三:解析字符串
   27    // printf("echo: %s\n",command);
   28     const char* sep = " ";
   29     argv[0] = strtok(command,sep);//传入字符串,以sep为分隔符
   30     int i = 1;
W> 31     while(argv[i] = strtok(NULL,sep))
   32     {
   33       i++;
   34     }
   35 
   36     //步骤四:检测命令是否需要shell本身执行的内建命令
   37     if(strcmp("cd",argv[0]) == 0){
   38       if(argv[1] != NULL) chdir(argv[1]);
   39       continue;
   40     }
   41 
   42 
   43     //步骤五:执行第三方命令
   44     if(fork() == 0)
   45     {
   46       //child
   47       execvp(argv[0],argv);
   48       //如果返回了就说明失败了,那么我们就终止进程
   49       exit(1);
   50     }
   51 
   52     int status = 0;
   53     waitpid(-1,&status,0);
   54     printf("exit code: %d\n",(status >> 8)&0xFF);
   55 //  while()
   56 //   {
   57 //     //如果是老串的话,第一个变量就传NULL
   58 //     argv[i] = strtok(NULL,sep);
   59 //     if(argv[i] == NULL)
   60 //       break;
   61 //     i++;
   62 //   }
   63 //    for(i = 0; argv[i];i++)
   64 //    {
   65 //      printf("argv[%d]:%s\n",i,argv[i]);
   66 //    }
   67 
   68   }
   69   return 0;
   70 }
 

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 12:31:28  更:2022-04-29 12:32:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 19:43:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码