【操作系统】06.进程控制
一、进程创建
1.1 认识fork函数
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
进程调用fork,当控制转移到内核中的fork代码后,内核将
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程
- 添加子进程到系统进程列表当中
- fork返回,开始调度器调度
1.2 写时拷贝
通常,父子代码共享,父子在不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副
本。具体见下图:
其实,我们使用fork()函数创建子进程时,操作系统会将父进程数据权限置为只读。后续当我们想要向子进程写入时,首先会触发系统错误,引起缺页中断,而后操作系统就会进行检测,当判断为该数据是可写入时发生写时拷贝。
进行写时拷贝时,首先会向操作系统申请内存空间进行拷贝,然后修改页表对应的物理内存,最后将读写权限恢复即可。整个过程都是由操作系统自主实现的。
二、进程终止
前面我们有谈到过,当进程退出时会返回父进程或操作系统一个退出码。我们可以使用$?来查看最近一个进程的退出码,用0表示正常退出,非零表示各种各样的退出原因(可以自己设置)。
2.1 进程终止的方式
main函数返回
#include<stdio.h>void func()
{printf("Hello World!\n");
}int main()
{func();return 0;
}
[caryon@VM-24-10-centos lesson16]$ ./code
Hello World!
[caryon@VM-24-10-centos lesson16]$ echo $?
0
调用exit
#include<stdio.h>
#include<stdlib.h>void func()
{printf("Hello World!\n");exit(100);
}int main()
{func();printf("process is done!\n");return 0;
}
[caryon@VM-24-10-centos lesson16]$ ./code
Hello World!
[caryon@VM-24-10-centos lesson16]$ echo $?
100
调用_exit
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void func()
{printf("Hello World!\n");_exit(100);
}int main()
{func();printf("process is done!\n");return 0;
}
[caryon@VM-24-10-centos lesson16]$ ./code
Hello World!
[caryon@VM-24-10-centos lesson16]$ echo $?
100
Ctrl+c
这个我们很常用了,就不多赘述了。
exit和_exit有什么区别?
我们通过一个小实验发现exit会将缓冲区刷新,而_exit则不会。
这一点区别也与他们的特性有关:_exit是系统调用接口,而exit是对_exit和输出缓冲区的封装。这一点也正好的说明了缓冲区不是系统层的概念,而是语言层的概念,缓冲区一定不在操作系统上。
三、进程等待
3.1 为什么要有进程等待
- 之前的博客讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
- 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
- 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.2 进程等待的方法
wait
#include<sys/types.h>
#include<sys/wait.h>pid_t wait(int* status);//返回值:
// 成功返回被等待进程pid,失败返回-1。
//参数:
// 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
这个函数用以让父进程等待任意一个子进程结束,等待的时候,子进程不退,父进程就要阻塞在wait函数内部。
可以回收僵尸状态的子进程。
实例:
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>void Worker()
{int cnt =10;while (cnt--){printf("I am child process, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt);sleep(1);}
}int main()
{pid_t id = fork();if (id == 0){// childWorker();exit(0);}else{// fatherpid_t rid = wait(NULL);if (rid > 0)printf("wait success\n");else if(rid == -1)printf("wait fail\n");while(1){printf("haha\n");sleep(1);}}return 0;
}
[caryon@VM-24-10-centos ~]$ ps axj |head -1 ;ps axj |grep code |grep -v grepPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
30280 16225 16225 30280 pts/0 16225 S+ 1001 0:00 ./code
16225 16226 16225 30280 pts/0 16225 S+ 1001 0:00 ./code
[caryon@VM-24-10-centos ~]$ ps axj |head -1 ;ps axj |grep code |grep -v grepPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
30280 16225 16225 30280 pts/0 16225 S+ 1001 0:00 ./code
I am child process, pid: 16226, ppid: 16225, cnt: 9
I am child process, pid: 16226, ppid: 16225, cnt: 8
I am child process, pid: 16226, ppid: 16225, cnt: 7
I am child process, pid: 16226, ppid: 16225, cnt: 6
I am child process, pid: 16226, ppid: 16225, cnt: 5
I am child process, pid: 16226, ppid: 16225, cnt: 4
I am child process, pid: 16226, ppid: 16225, cnt: 3
I am child process, pid: 16226, ppid: 16225, cnt: 2
I am child process, pid: 16226, ppid: 16225, cnt: 1
I am child process, pid: 16226, ppid: 16225, cnt: 0
wait success
haha
haha
haha
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:
// Pid=-1,等待任一个子进程。与wait等效。
// Pid>0.等待其进程ID与pid相等的子进程。
// status:
// WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
// WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
// options:
// WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
这个函数与上述函数可以不同,它可以设置是否为阻塞等待,只需要将options置为非零就可以实现非阻塞等待。
实例
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>void Worker()
{int cnt =10;while (cnt--){printf("I am child process, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt);sleep(1);}
}int main()
{pid_t id = fork();if (id == 0){// childWorker();exit(0);}else{// fatherpid_t rid = waitpid(-1,NULL,WNOHANG);if (rid > 0)printf("wait success\n");else if(rid == -1)printf("wait fail\n");while(1){printf("haha\n");sleep(1);}}return 0;
}
[caryon@VM-24-10-centos ~]$ ps axj |head -1 ;ps axj |grep code |grep -v grepPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
30280 20747 20747 30280 pts/0 20747 S+ 1001 0:00 ./code
20747 20748 20747 30280 pts/0 20747 S+ 1001 0:00 ./code
[caryon@VM-24-10-centos ~]$ ps axj |head -1 ;ps axj |grep code |grep -v grepPPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
30280 20747 20747 30280 pts/0 20747 S+ 1001 0:00 ./code
haha
I am child process, pid: 20748, ppid: 20747, cnt: 9
haha
I am child process, pid: 20748, ppid: 20747, cnt: 8
haha
I am child process, pid: 20748, ppid: 20747, cnt: 7
I am child process, pid: 20748, ppid: 20747, cnt: 6
haha
haha
I am child process, pid: 20748, ppid: 20747, cnt: 5
haha
I am child process, pid: 20748, ppid: 20747, cnt: 4
haha
I am child process, pid: 20748, ppid: 20747, cnt: 3
haha
I am child process, pid: 20748, ppid: 20747, cnt: 2
I am child process, pid: 20748, ppid: 20747, cnt: 1
haha
haha
非阻塞等待状态就允许父进程干自己的事情。
3.3 获取子进程的status
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
- status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(status的低16位用以表示退出信息,其中该16位的高8位用以存储退出状态信息,低7位用以存储终止信号信息):
实例:
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>int main()
{pid_t pid = fork();if (pid < 0)perror("fork"),exit(1);if ( pid == 0 ){sleep(20);exit(10);} else {int st;int ret = wait(&st);if ( ret > 0 && ( st & 0X7F ) == 0 ){ // 正常退出printf("child exit code:%d\n", (st>>8)&0XFF);} else if( ret > 0 ) { // 异常退出printf("sig code : %d\n", st&0X7F );}}
}
四、进程程序替换
4.1 替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
4.2 exec系列函数
#include <unistd.h>`
//语言封装接口
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
//系统接口
int execve(const char *path, char *const argv[], char *const envp[]);//这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
//如果调用出错则返回-1
//所以exec函数只有出错的返回值而没有成功的返回值。
这些函数看似很多,但是我们掌握了规律就好记了:
- l(list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量
单进程的例子
#include <stdio.h>
#include <unistd.h>int main()
{execl("/usr/bin/ls","-l","-a",NULL);return 0;
}
[caryon@VM-24-10-centos linux]$ ./code
. code .git lesson11 lesson13 lesson15 lesson2 lesson4 lesson6 lesson8 工具.png 权限.png
.. code.c lesson10 lesson12 lesson14 lesson16 lesson3 lesson5 lesson7 lesson9 指令.png
我们如果仅仅是单进程的话一旦execl错误就会导致我们的进程崩溃,因此我们都是使用子进程在执行我们的进程程序替换的。
多进程的例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>const char* const argv[] = {"ls","-l","-a",NULL
};int main()
{pid_t id = fork();if(id == 0){// childprintf("I am a child , my PID:%d\n",getpid());execl("/bin/ls","-aln",NULL);exit(0);} else {pid_t rid = waitpid(-1,NULL,0);if (rid > 0){printf("wait succes!! PID:%d\n",rid);}}return 0;
}
[caryon@VM-24-10-centos linux]$ ./code
I am a child , my PID:10628
code lesson10 lesson12 lesson14 lesson16 lesson3 lesson5 lesson7 lesson9 指令.png
code.c lesson11 lesson13 lesson15 lesson2 lesson4 lesson6 lesson8 工具.png 权限.png
wait succes!! PID:10628
相关文章:

【操作系统】06.进程控制
一、进程创建 1.1 认识fork函数 在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。 进程调用fork,当控制转移到内核中的fork代码后,内核将 分配新的内存块和内核数据结构…...
16天自制CppServer-day02
day02-设置错误与异常处理机制 上一天我们写了一个客户端与服务器通过socket进行连接,对socket,bind,listen,accept,connect等函数,我们都设想程序完美地、没有任何异常地运行,但显然这不现实,应该设置出现异常的处理机制&#x…...

时空智友企业流程化管控系统uploadStudioFile接口存在任意文件上传漏洞
免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. 时空智友…...

Linux 中文件的权限说明
目录 一:文件权限类型二:默认权限管理1. 查看当前用户的umask值2. 修改当前用户的umask值3. 根据umask计算默认权限 三:普通权限管理1. 三种普通权限说明1.1 对于非目录文件来说1.2 对于目录文件来说 2. 查看某个文件的权限信息2.1 使用 ls -…...

MySql数据库中数据类型
本篇将介绍在 MySql 中的所有数据类型,其中主要分为四类:数值类型、文本和二进制类型、时间日期、String 类型。如下(图片来源:MySQL数据库): 目录如下: 目录 数值类型 1. 整数类型 2. …...

Godot中的信号
目录 概念 signal connect方法连接Callable 信号要求参数 查看信号 连接信号 监听信号 Button - text属性 pressed 连接源 「按钮」的信号连接 使用代码,将方法与信号相连接 节点的connect方法 节点直接使用emit_signal方法通过字符串的方式触发信号…...
vba学习系列(8)--指定列单元格时间按时间段计数
系列文章目录 文章目录 系列文章目录前言一、背景二、VBA总结 前言 一、背景 时间格式:00:00:00 时间段格式:00:00:00 - 01:00:00 计数N列单元格时间位于时间段内的行数 二、VBA 代码如下(示例): Sub AssignTimeSeg…...

大型企业软件开发是什么样子的? - Web Dev Cody
引用自大型企业软件开发是什么样子的? - Web Dev Cody_哔哩哔哩_bilibili 一般来说 学技术的时候 我们会关注 开发语言特性 ,各种高级语法糖,底层技术 但是很少有关注到企业里面的开发流程,本着以终为始(以就业为导向…...

【stm32】DMA的介绍与使用
DMA的介绍与使用 1、DMA简介2、存储器映像3、DMA框图4、DMA基本结构5、DMA请求6、数据宽度与对齐7、数据转运DMA(存储器到存储器的数据转运)程序编写: 8、ADC连续扫描模式DMA循环转运DMA配置:程序编写: 1、DMA简介 DM…...

哈希表的魔力
哈希表与字典 普遍存在一种误解,认为“哈希表”和“字典”这两个术语可以互换。这种观念从根本上是不准确的,至少在计算机科学领域是如此。 字典是将键映射到值的数据结构的一般概念。而哈希表是字典的具体实现。 本质上,字典扮演着一个总体…...

《YOLO 目标检测》—— YOLO v3 详细介绍
!!!!!!!!!!!!!还未写完!!!!!!!…...

WNN 多模态整合 | Seurat 单细胞多组学整合流程
测试环境:CentOS7.9, R4.3.2, Seurat 4.4.0, SeuratObject 4.1.4 2024.10.23 # WNN library(ggplot2) library(dplyr) library(patchwork)1. 导入数据 (1). load counts of RNA and protein dyn.load(/home/wangjl/.local/lib/libhdf5_hl.so.100) library(hdf5r)…...

【Linux】磁盘文件系统(inode)、软硬链接
文章目录 1. 认识磁盘1.1 磁盘的物理结构1.2 磁盘的逻辑结构 2. 引入文件系统2.1 EXT系列文件系统的分区结构2.2 inode 3. 软硬链接3.1 软链接3.2 硬链接 在讲过了内存文件系统后,我们可以知道文件分为两种: 打开的文件(内存中)未…...

网安加·百家讲坛 | 徐一丁:金融机构网络安全合规浅析
作者简介:徐一丁,北京小西牛等保软件有限公司解决方案部总监,网络安全高级顾问。2000年开始从事网络安全工作,主要领域为网络安全法规标准研究、金融行业安全咨询与解决方案设计、信息科技风险管理评估等。对国家网络安全法规标准…...

九、pico+Unity交互开发——触碰抓取
一、VR交互的类型 Hover(悬停) 定义:发起交互的对象停留在可交互对象的交互区域。例如,当手触摸到物品表面(可交互区域)时,视为触发了Hover。 Grab(抓取) 概念ÿ…...

老机MicroServer Gen8再玩 OCP万兆光口+IT直通
手上有一台放了很久的GEN8微型服务器,放了很多年,具体什么时候买的我居然已经记不清了 只记得开始装修的时候搬家出去就没用了,结果搬出去有了第1个孩子,孩子小的时候也没时间折腾,等孩子大一点的时候,又有…...

jmeter 从多个固定字符串中随机取一个值的方法
1、先新增用户参数,将固定值设置为不同的变量 2、使用下面的函数,调用这写变量 ${__RandomFromMultipleVars(noticeType1|noticeType2|noticeType3|noticeType4|noticeType5)} 3、每次请求就是随机取的值了...

priority_queue (优先级队列的使用和模拟实现)
使用 priority_queue 优先级队列与 stack 和 queue 一样,也是一个容器适配器,其底层通过 vector 来实现的。与 stack 和 queue 不同的是,它的第一个元素总是它所包含的元素中最大或最小的一个。 也就是说,优先级队列就是数据结…...
VisionPro 手部骨骼跟踪 Skeletal Hand Tracking 虚拟首饰
骨骼手部跟踪由XR Hands Package中的Hand Subsystem提供。使用场景中的Hand Visualizer组件,用户可以显示玩家手部的蒙皮网格或每个关节的几何图形,以及用于基于手部物理交互的物理对象。用户可以直接针对Hand Subsystem编写 C# 脚本,以推断骨…...

class 9: vue.js 3 组件化基础(2)父子组件间通信
目录 父子组件之间的相互通信父组件传递数据给子组件Prop为字符串类型的数组Prop为对象类型 子组件传递数据给父组件 父子组件之间的相互通信 开发过程中,我们通常会将一个页面拆分成多个组件,然后将这些组件通过组合或者嵌套的方式构建页面。组件的嵌套…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...