当前位置: 首页 > news >正文

【操作系统】06.进程控制

一、进程创建

1.1 认识fork函数

在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
在这里插入图片描述

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

  1. 分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构内容拷贝至子进程
  3. 添加子进程到系统进程列表当中
  4. 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函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 进程调用fork&#xff0c;当控制转移到内核中的fork代码后&#xff0c;内核将 分配新的内存块和内核数据结构…...

16天自制CppServer-day02

day02-设置错误与异常处理机制 上一天我们写了一个客户端与服务器通过socket进行连接&#xff0c;对socket,bind,listen,accept,connect等函数&#xff0c;我们都设想程序完美地、没有任何异常地运行&#xff0c;但显然这不现实&#xff0c;应该设置出现异常的处理机制&#x…...

时空智友企业流程化管控系统uploadStudioFile接口存在任意文件上传漏洞

免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. 时空智友…...

Linux 中文件的权限说明

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

MySql数据库中数据类型

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

Godot中的信号

目录 概念 signal connect方法连接Callable 信号要求参数 查看信号 连接信号 监听信号 Button - text属性 pressed 连接源 「按钮」的信号连接 使用代码&#xff0c;将方法与信号相连接 节点的connect方法 节点直接使用emit_signal方法通过字符串的方式触发信号…...

vba学习系列(8)--指定列单元格时间按时间段计数

系列文章目录 文章目录 系列文章目录前言一、背景二、VBA总结 前言 一、背景 时间格式&#xff1a;00:00:00 时间段格式&#xff1a;00:00:00 - 01:00:00 计数N列单元格时间位于时间段内的行数 二、VBA 代码如下&#xff08;示例&#xff09;&#xff1a; Sub AssignTimeSeg…...

大型企业软件开发是什么样子的? - Web Dev Cody

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

【stm32】DMA的介绍与使用

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

哈希表的魔力

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

《YOLO 目标检测》—— YOLO v3 详细介绍

&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;还未写完&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xf…...

WNN 多模态整合 | Seurat 单细胞多组学整合流程

测试环境&#xff1a;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 硬链接 在讲过了内存文件系统后&#xff0c;我们可以知道文件分为两种&#xff1a; 打开的文件&#xff08;内存中&#xff09;未…...

网安加·百家讲坛 | 徐一丁:金融机构网络安全合规浅析

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

九、pico+Unity交互开发——触碰抓取

一、VR交互的类型 Hover&#xff08;悬停&#xff09; 定义&#xff1a;发起交互的对象停留在可交互对象的交互区域。例如&#xff0c;当手触摸到物品表面&#xff08;可交互区域&#xff09;时&#xff0c;视为触发了Hover。 Grab&#xff08;抓取&#xff09; 概念&#xff…...

老机MicroServer Gen8再玩 OCP万兆光口+IT直通

手上有一台放了很久的GEN8微型服务器&#xff0c;放了很多年&#xff0c;具体什么时候买的我居然已经记不清了 只记得开始装修的时候搬家出去就没用了&#xff0c;结果搬出去有了第1个孩子&#xff0c;孩子小的时候也没时间折腾&#xff0c;等孩子大一点的时候&#xff0c;又有…...

jmeter 从多个固定字符串中随机取一个值的方法

1、先新增用户参数&#xff0c;将固定值设置为不同的变量 2、使用下面的函数&#xff0c;调用这写变量 ${__RandomFromMultipleVars(noticeType1|noticeType2|noticeType3|noticeType4|noticeType5)} 3、每次请求就是随机取的值了...

priority_queue (优先级队列的使用和模拟实现)

使用 priority_queue 优先级队列与 stack 和 queue 一样&#xff0c;也是一个容器适配器&#xff0c;其底层通过 vector 来实现的。与 stack 和 queue 不同的是&#xff0c;它的第一个元素总是它所包含的元素中最大或最小的一个。 也就是说&#xff0c;优先级队列就是数据结…...

VisionPro 手部骨骼跟踪 Skeletal Hand Tracking 虚拟首饰

骨骼手部跟踪由XR Hands Package中的Hand Subsystem提供。使用场景中的Hand Visualizer组件&#xff0c;用户可以显示玩家手部的蒙皮网格或每个关节的几何图形&#xff0c;以及用于基于手部物理交互的物理对象。用户可以直接针对Hand Subsystem编写 C# 脚本&#xff0c;以推断骨…...

class 9: vue.js 3 组件化基础(2)父子组件间通信

目录 父子组件之间的相互通信父组件传递数据给子组件Prop为字符串类型的数组Prop为对象类型 子组件传递数据给父组件 父子组件之间的相互通信 开发过程中&#xff0c;我们通常会将一个页面拆分成多个组件&#xff0c;然后将这些组件通过组合或者嵌套的方式构建页面。组件的嵌套…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...