【操作系统】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)
    {
        // child
        Worker();
        exit(0);
    }
    else
    {
        // father
        pid_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 grep
 PPID   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 grep
 PPID   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)
    {
        // child
        Worker();
        exit(0);
    }
    else
    {
        // father
        pid_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 grep
 PPID   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 grep
 PPID   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)
    {
       // child
       printf("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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/898714.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Aspose.PDF功能演示:使用 JavaScript 从 PDF 中提取文本

在数据提取、业务文档自动化和文本挖掘方面&#xff0c;使用 JavaScript 从PDF中提取文本非常有用。它允许开发人员自动执行从 PDF 收集信息的过程&#xff0c;从而显著提高处理大量文档的生产力和效率。在这篇博文中&#xff0c;我们将学习如何使用 JavaScript 从 PDF 中提取文…

人工智能的未来应用与发展前景

随着人工智能&#xff08;AI&#xff09;技术的快速进步&#xff0c;我们正亲历着它在各行各业中带来的巨大变革。无论是医疗、企业管理&#xff0c;还是日常生活&#xff0c;AI 技术都在改变着我们的工作和生活方式。那么&#xff0c;人工智能的应用前景究竟如何&#xff1f;它…

【消息队列】RabbitMQ实现消费者组机制

目录 1. RabbitMQ 的 发布订阅模式 2. GRPC 服务间的实体同步 2.1 生产者服务 2.2 消费者服务 3. 可靠性 3.1 生产者丢失消息 3.2 消费者丢失消息 3.3 RabbitMQ 中间件丢失消息 1. RabbitMQ 的 发布订阅模式 https://www.rabbitmq.com/tutorials/tutorial-three-go P 生…

winUI3 c++ 入门 2、 样式

目录 一、winUI3 基本概念及样式 1、边距 2、如何使用样式 1)、布局控件内定义样式 2)、APP.xmal定义全局样式 3)、单独的样式文件 3.1)、新增字典资源 xmal 3.2)、在里面设置样式 3.3)、引用样式 3、更多样式修改 1)、修改默认属性 2)、修改所有的默认颜色…

垃圾收集器与内存分配机制(一)

目录 一、为什么我们要去了解垃圾收集和内存分配 二、对象已死&#xff1f; 1. 引用计数算法 2. 可达性分析算法 3. 再谈引用 4. 生存还是死亡 5. 回收方法区 三、垃圾收集算法 1. 简介 2. 分代收集理论 2.1. 弱分代/强分代假说 2.2. 前面两代假说的缺陷 3. 标记-清…

智能去毛刺:2D视觉引导机器人如何重塑制造业未来

机器人技术已经深入到各个工业领域中&#xff0c;为制造业带来了前所未有的变革。其中&#xff0c;2D视觉引导机器人技术以其精准、高效的特点&#xff0c;在去毛刺工艺中发挥着越来越重要的作用。本文将为您介绍2D视觉引导机器人技术的基本原理及其在去毛刺工艺中的应用&#…

blender 理解 积木组合 动画制作 学习笔记

一、学习blender视频教程链接 案例2&#xff1a;积木组合_动画制作_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Bt4y1E7qn?vd_sourced0ea58f1127eed138a4ba5421c577eb1&p10&spm_id_from333.788.videopod.episodes 二、说明 之前已经学习了如何制作积木组…

20 Shell Script输入与输出

标出输入、标准输出、错误输出 一、程序的基本三个IO流 一&#xff09;文件描述符 ​ 任何程序在Linux系统中都有3个基本的文件描述符 ​ 比如: ​ cd/proc/$$/fd ​ 进入当前shell程序对于内核在文件系统的映射目录中: [rootlocalhost ~]# cd /proc/$$/fd [rootlocalhos…

Ubuntu22.04环境搭建MQTT服务器

官网&#xff1a; https://mosquitto.org 1.引入库 sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa2.升级安装工具 sudo apt-get update 3.安装 sudo apt-get install mosquitto 4.安装客户端 sudo apt-get install mosquitto-clients5.添加修改配置文件 进…

微信小程序上传图片添加水印

微信小程序使用wx.chooseMedia拍摄或从手机相册中选择图片并添加水印&#xff0c; 代码如下&#xff1a; // WXML代码&#xff1a;<canvas canvas-id"watermarkCanvas" style"width: {{canvasWidth}}px; height: {{canvasHeight}}px;"></canvas&…

【Linux】冯诺依曼体系结构 OS的概念

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 前言废话&#xff1a…

将java项目jar包打包成exe服务

1.结构展示 2.注意事项 前提: 环境准备:jdk8 和 .net支持 { 1.控制面板》程序和功能》启用和关闭windows功能》.net的勾选》2.jdk8自行百度安装环境3.其他项目必须的软件环境安装等&#xff08;数据库...&#xff09; }第一次准备: 1.将打包好的jar包放到premiumServices.exe…

销冠教你如何转化观望客户

在销售实践中&#xff0c;常会遇到这样的场景&#xff1a;客户对我们的提案表现出极大的兴趣&#xff0c;但在执行阶段却显得迟疑&#xff0c;频繁表示“还需观望&#xff0c;再考虑”。这种态度不仅拖慢了项目进度&#xff0c;甚至可能导致项目完全停滞&#xff0c;从而错失宝…

Spring Boot技术栈在论坛网站开发中的应用

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

FLUX.1-dev-LoRA模型:用一张卡通图片总结4张真实人物照片One-Click-Creative-Template

在这个数字时代&#xff0c;创意图像生成正成为我们生活中不可或缺的一部分。Shakker Labs 推出的 FLUX.1-dev-LoRA-One-Click-Creative-Template 为我们带来了一个全新的体验。这款创新的模板模型专为文本生成图像设计&#xff0c;让您能够轻松创造出令人惊艳的照片。 模型功能…

听泉鉴宝在三个月前已布局商标注册!

近日“听泉鉴宝”以幽默的风格和节目效果迅速涨粉至2500多万&#xff0c;连线出现“馆藏文物”和“盗墓现场”等内容&#xff0c;听泉鉴宝早在几个月前已布局商标注册。 据普推知产商标老杨在商标局网站检索发现&#xff0c;“听泉鉴宝”的主人丁某所持股的江苏灵匠申请了三十…

qt QNetworkProxy详解

一、概述 QNetworkProxy通过设置代理类型、主机、端口和认证信息&#xff0c;可以使应用程序的所有网络请求通过代理服务器进行。它支持为Qt网络类&#xff08;如QAbstractSocket、QTcpSocket、QUdpSocket、QTcpServer、QNetworkAccessManager等&#xff09;配置网络层代理支持…

mysql innodb 引擎如何直接复制数据库文件?

mysql innodb 引擎如何直接复制数据库文件&#xff1f;介绍如下&#xff1a; 1、首先找到数据库文件所在位置 一般可以看my.conf/my.ini配置的文件的“datadir” 看示例&#xff1a; “MAMP”在Macos下的数据库文件位置&#xff1a; /Library/Application Support/appsolu…

展会亮点回顾|HMS汽车工业通信解决方案

2024 汽车测试及质量监控博览会&#xff08;中国&#xff09;&#xff08;Testing Expo China – Automotive&#xff09;于 8 月 28 日至 30 日在上海世博展览馆顺利举行。作为汽车测试技术领域的顶级盛会&#xff0c;来自全球的行业领袖和技术专家齐聚一堂&#xff0c;共同探…

值得细读的8个视觉大模型生成式预训练方法

大语言模型的进展催生出了ChatGPT这样的应用&#xff0c;让大家对“第四次工业革命”和“AGI”的来临有了一些期待&#xff0c;也作为部分原因共同造就了美股2023年的繁荣。LLM和视觉的结合也越来越多&#xff1a;比如把LLM作为一种通用的接口&#xff0c;把视觉特征序列作为文…