进程与线程(一)

进程与线程(一)

  • 理解什么是并发编程
  • 进程的相关概念
    • 什么是进程
    • 对比进程和程序
    • 理解进程是一个独立的可调度的任务
    • 理解进程是程序执行和资源管理的最小单位
    • 进程状态转换图
    • 进程的种类
  • 进程相关命令
    • 进程状态标志
    • ps命令
      • -aux:
      • -axj:(可以查看到进程的PPID)
      • pstree
      • kill命令
  • 进程相关的系统调用
    • 进程创建
      • fork()
      • vfrok()
      • fork和vfork的区别
      • 获取进程ID和父ID的方法
      • 进程的退出
  • 僵尸进程和孤儿进程
    • 僵尸进程
    • 孤儿进程
    • 回收僵尸进程的方法
      • wait阻塞回收子进程的退出资源
      • waitpid非阻塞回收子进程的退出资源

理解什么是并发编程

并发是什么?
—》多个任务同时运行
任务是什么?
—》就是一个应用程序
思考:程序是如何产生的?
—》程序:一组能被计算机直接识别的有序指令的集合
所以:程序是被编译生成的 eg:gcc test.c -o App (App就是一个程序)
程序在外存(U盘,硬盘)上存储(静态的概念)
一个程序一旦被运行起来,就会产生一个进程(动态的:被创建,被调度,被消亡)
总结:Linux下的多任务编程就是多进程/多线程去协同工作,将某一个事情搞定的过程该过程避免不了多进程/多线程之间数据交互的问题(进程间通信的技术)

进程的相关概念

什么是进程

概念:进程就是程序的一次动态执行过程

对比进程和程序

程序:一组存储在磁盘上的有序指令的集合,是一个静态且没有任何执行的概念
进程:运行一个程序,就会拥有一个进程,它是程序的一次动态执行过程,动态性:创建,调度,消亡

理解进程是一个独立的可调度的任务

一个进程是一个独立的个体,意味着拥有自己专属的地址空间
地址空间图解:
在这里插入图片描述
调度:调用进程/线程,调用动作是由CPU来完成的!
再次理解:
CPU调用进程/线程本质:执行它!
思考:Linux下如何实现多任务(并发编程)的?(借助于CPU调度)
注意:单核的CPU同一时刻只能处理一个进程/线程,并发如何实现?
—》Linux下实现并发的思想:引入“时间片”的概念时间片?
–》每一个任务,操作系统为其分配了一段执行的时间,在这段时间之内处理你,超时则下次再处理,时间片很小,小到用户都可能感知不到时间片的概念,因此每一个任务都会有一个时间片,他们之间采用时间片轮转的概念,被加入到一个循环队列中(注意:该资源队列并不遵循先进先出的原则,多个任务的调度的是随机的,且存在优先级的概念!),多个任务(程序的执行过程)存在状态的切换。

理解进程是程序执行和资源管理的最小单位

程序执行的最小单位:每运行一个程序,就会产生一个进程
资源管理的最小单位:每一个进程都会拥有一个独立的地址空间(资源)

进程状态转换图

进程的状态:运行态,就绪态,等待态,停止态,死亡态,僵尸态
图解:运行态,等待态,死亡态
在这里插入图片描述

进程的种类

(1)交互进程:该进程需要人机交互,用户需要给该进程一个信息的反馈。(shell终端)
(2)批处理进程:也称批处理文本或者批处理脚本,他们采用轮询的概念去依次执行。
(3)守护进程:也称“精灵进程”,“监测进程”,”后台进程“,不受终端的控制!运行在后台。

进程相关命令

进程状态标志

在这里插入图片描述

ps命令

作用:打印出当前运行的进程
使用ps命令时,一般都会搭配选项来使用:
常用的选项

-aux:

在这里插入图片描述

-axj:(可以查看到进程的PPID)

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

pstree

作用:以树形关系呈现当前终端所有进程的所属关系
在这里插入图片描述

kill命令

作用:发送信号
常用的:杀死进程
方法:kill -9 pid; //pid为需要杀死的进程ID号
Linux下共有64个信号:
在这里插入图片描述

进程相关的系统调用

明确:进程都是被进程创建出来的
1号进程:init进程
init进程创建出一个systemd进程!
终端也是一个进程,也是被创建出来的
故:想要得到一个子进程,就必须去创建!!!

进程创建

fork()

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:创建一个进程
参数:无参
返回值:pid_t是int的别名,含义:
小于0,代表创建子进程失败,可以打印错误码查看原因
等于0,是在子进程中返回的
大于0,是在父进程中返回的,且含义为子进程的ID号(PID)

在这里插入图片描述
验证子进程的数据段是也是拷贝于父进程的(fork()):
在这里插入图片描述
在这里插入图片描述
验证fork之前,子进程会悉数拷贝父进程的所有内容:fork之前父进程有的,子进程都会拷贝一份

在这里插入图片描述
在这里插入图片描述
fork()的原理:

调用进程(称为父进程)通过复制自己,创建出一个新进程(称为子进程),子进程获得父进程的数据空间、堆和栈的副本,即父子进程的空间是独立的,创建出的子进程从fork返回处开始运行。

总结fork():

fork创建的子进程:
1、会完美拷贝父进程中的所有资源(堆栈段,代码段,数据段…)
2、子进程从fork的下一句话开始,会存在父子进程,为了区分两个进程到底谁在执行?
—》通过返回值来判断:等于0(子进程在执行)。大于0(父进程在执行)
3、子进程永远都是在fork的下一句开始执行的!(会用到fork之前父进程定义过的一些资源。)、
4、父子进程的执行次序不确定的!
5、父子进程的地址空间独立,互不影响,父子进程可以被CPU进行调度。

vfrok()

#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
用法同fork()!!!

vfork()的原理

vfork()函数创建新进程时并不复制父进程的地址空间,刚开始是运行在父进程的地址空间上的,且保障了子进程会优先被执行,当子进程执行了exec或者exit之后,父进程才有可能被调度。如果在调用exec或者exit之前,子进程依赖于父进程的进一步动作,则会导致死锁!
注意:使用vfork函数之后,子进程内部必须使用exec或者exit函数,否则会造成死锁的现象。

fork和vfork的区别

1、
fork():子进程拷贝父进程的数据段,代码段
vfork():子进程与父进程共享数据段
2、
fork()父子进程的执行次序不确定
vfork()保证子进程先运行,在调用exec或exit之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。
3、
vfork()保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁,所以使用vfork创建的子进程必须使用exec重新启动一个新的程序或者exit()使得子进程退出

获取进程ID和父ID的方法

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);//获取当前进程的ID号
pid_t getppid(void);//获取当前进程的父进程ID号

以上两个函数的返回值就是获取的ID号的结果!!!

进程的退出

_exit()函数的作用最为简单:直接使进程终止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;
exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。
exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是图中的"清理I/O缓冲"一项。

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

僵尸进程和孤儿进程

僵尸进程

子进程先于父进程退出,而父进程没有回收子进程的退出资源(一小部分的PCB:struct_task的结构体),此时的子进程就会沦为僵尸进程!
僵尸进程的危害:内存泄漏,因此需要避免僵尸进程的产生!
如何避免僵尸进程产生?
----》子进程的退出资源本应该是父进程来回收,因此将回收子进程的退出资源这件事让父进程使用某种手段进行处理即可!

孤儿进程

父进程先于子进程退出,此时的子进程会沦为孤儿进程,会被systemd进程收养,孤儿进程没有任何坏处

回收僵尸进程的方法

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

wait阻塞回收子进程的退出资源

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
	//定义局部变量
	int a = 0;
	//使用fork来创建一个子进程
	pid_t pid = fork();
	if(pid < 0)
	{
		perror("fork child process error");
		return -1;
	}
	else if(0 == pid)
	{
		//子进程在执行代码
		while(1)
		{
			a++;
			printf("子进程:a = %d\n",a);
			printf("I am child process!\n");
			sleep(1);
		}
	}
	else
	{
		//使用wait阻塞回收子进程的退出资源
		printf("等待回收子进程中......\n");
		pid_t exitPID = wait(NULL);//阻塞
		printf("exitPID = %d的子进程已被回收!\n", exitPID);
		//父进程在执行代码
		while(1)
		{
			a++;
			printf("父进程:a = %d\n",a);
			printf("I am parent process!\n");
			sleep(1);
		}
	}
	printf("test!!!\n");
	return 0;
}

waitpid非阻塞回收子进程的退出资源

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	//定义局部变量
	int a = 0;
	//使用fork来创建一个子进程
	pid_t pid = fork();
	if(pid < 0)
	{
		perror("fork child process error");
		return -1;
	}
	else if(0 == pid)
	{
	//子进程在执行代码
		while(1)
		{
			a++;
			printf("子进程:a = %d\n",a);
			printf("I am child process!\n");
			sleep(1);
			if(a >= 5)
			{
			exit(0);//子进程主动退出 返回-1此时对方查看的是255
			}
		}
	}
	else
	{
		//父进程在执行代码
		while(1)
		{
			printf("等待回收资源中...\n");
			//引入waitpid回收子进程的退出资源,关心子进程的退出状态
			int status;
			pid_t exitPID = waitpid(-1, &status, WNOHANG);
			if(exitPID < 0)
			{
				perror("waitpid error");
			}
			else if(0 == exitPID)
			{
				printf("未检测到有子进程结束...\n");
			}
			else
			{
				//判断子进程是正常退出还是异常退出
				if(WIFEXITED(status))
				{
					//进入if则代表正常退出
					printf("正常退出!\n");
				}
				else
				{
					printf("非正常退出!\n");
				}
				printf("已成功回收exitPID = %d的子进程资源!\n", \
				exitPID);
				//获取子进程的退出状态数值
				printf("退出状态数值:%d\n",WEXITSTATUS(status));
			}
			a++;
			printf("父进程:a = %d\n",a);
			printf("I am parent process!\n");
			sleep(1);
		}
	}
	printf("test!!!\n");
	return 0;
}

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

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

相关文章

【通信专题】I2C通信硬件概述

通信协议在组织设备之间通信时扮演着重要角色。它基于系统要求而以不同方式进行设计。此类协议具有明确的、为实现成功通信而协商一致的规则。 I2C历史 I2C,即Inter-Integrated Circuit,是一种常用的串行通信协议。I2C总线创建于1982年,由飞利浦公司设计,旨在利用简单、稳…

APISIX的安装与测试(springboot服务测试)

安装&#xff1a; 1.1安装依赖&#xff1a; curl https://raw.githubusercontent.com/apache/apisix/master/utils/install-dependencies.sh -sL | bash -1.2 安装 OpenResty yum-config-manager --add-repo https://openresty.org/package/centos/openresty.reposudo yum i…

Java关键字详解

文章目录 什么是关键字&#xff1f;数据类型&#xff08;10个&#xff09;byte、char、boolean、short、int、float、long、double、void、enum 流程控制&#xff08;12个&#xff09;if、else、do、while、for 、switch、case、assertbreak&#xff08;跳出循环&#xff09;co…

[有监督学习] 8.详细图解神经网络

神经网络 一直以来&#xff0c;人们都认为神经网络&#xff08;Neural Network&#xff0c;NN&#xff09;是模仿生物体的神经网络设计而成的。神经网络既可以用于回归&#xff0c;也可以用于分类&#xff0c;但在实际应用中常用于分类。基于神经网络的深 度学习因在图像识别和…

五分钟“手撕”栈

实现代码放开头&#xff0c;供大家学习与查阅 目录 一、实现代码 二、什么是栈 三、栈的常见操作 底层实现是链表。 入栈 出栈 四、Stack的使用 五、栈的习题 第一题 第二题 第三题 第四题 第五题 第六题 第七题 六、栈、虚拟机栈、栈帧的区别 目录 一、…

Python的第三方库OS库

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;OS/SHUTIL 的方法描述&#x1f680;OS/SHUTIL…

Streamsets-JDBC模式使用更新时间字段数据同步

StreamSets的开源地址&#xff1a;https://github.com/streamsets/datacollector-oss Streamsets官网地址&#xff1a;https://streamsets.com/ Streamsets文档地址&#xff1a;https://docs.streamsets.com/portal/datacollector/3.16.x/help/index.html 我又来写Streamsets了…

安全测试扫描利器-Burpsuite

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

全自动打包封箱机:解析其在产品质量与安全保障方面的作用

在当今快节奏的生产环境中&#xff0c;全自动打包封箱机以其高效、精准的特点&#xff0c;正逐渐成为生产线上的得力助手。它不仅提升了生产效率&#xff0c;更在产品质量与安全保障方面发挥着举足轻重的作用。星派将详细解析全自动打包封箱机在产品质量与安全保障方面的作用。…

自监督表示学习和神经音频合成实现语音修复

关键词&#xff1a;语音修复、自监督模型、语音合成、语音增强、神经声码器 语音和/或音频修复的目标是增强局部受损的语音和/或音频信号。早期的工作基于信号处理技术&#xff0c;例如线性预测编码、正弦波建模或图模型。最近&#xff0c;语音/音频修复开始使用深度神经网络&a…

Qt | QSplitter(分离器或分隔符)、QSplitterHandle 类(分界线)

​01、一、QSplitter 类(分离器) 1、QSplitter 类继承自 QFrame 类,也就是说该类是一个带有边框的可视部件。 2、QSplitter 类实现分离器,分离器用于分离两个部件,用户可通过拖动部件之间的分界线来调整子部件的大小。 3、QSplitter 的原理(见上图):QSplitter 的实现原理…

AWS中国峰会2024 半日游

亚马逊云科技中国峰会于2024年5月29-30日在上海举办 今年就去了半天&#xff0c;去年也是去过的&#xff0c;不过今年的活动个人感觉比去年略微凌乱了一点。 今年的峰会方向和去年一致&#xff0c;均是AI方向的各项内容&#xff08;基础架构、安全、服务、游戏、驾驶、各行各…

浅谈防勒索病毒的关键

主机加固能否做好防勒索病毒的工作&#xff0c;一直是网络安全领域的重要议题。随着信息技术的飞速发展&#xff0c;勒索病毒等网络威胁层出不穷&#xff0c;对企业和个人数据安全构成了严重威胁。因此&#xff0c;如何通过主机加固提升安全防护能力&#xff0c;防止勒索病毒的…

大模型管理工具Ollama搭建及整合springboot

目录 一、Ollama介绍 1.1 什么是Ollama 1.2 Ollama特点与优势 二、Ollama本地部署 2.1 版本选择 2.2 下载安装包 2.3 执行安装 2.4 Ollama常用命令 三、使用Ollama部署千问大模型 3.1 千问大模型介绍 3.2 部署过程 四、springboot接入Ollama 4.1 引入Ollama依赖 4…

音频信号分析与实践

音频信号分析与实践课程,方便理解音频信号原理和过程 1.音频信号采集与播放 两种采样模式和标准的采样流程 人说话的声音一般在2kHz一下&#xff1a; 采样频率的影响&#xff1a;采样率要大于等于信号特征频率的2倍&#xff1b;一般保证信号完整&#xff0c;需要使用10倍以上的…

python找出100~999之间的水仙花数字

水仙花数字&#xff1a;个位&#xff0c;十位&#xff0c;百位的立方之和等于这个数本身 例如&#xff1a;153 1^35^33^3 for i in range(100, 1000):bw i // 100sw i % 100 // 10gw i % 10if bw ** 3 sw ** 3 gw ** 3 i:print(i)

SpringBoot读取json文件

使用SpringBoot读取json文件作为接口&#xff0c;前端Vue可以通过跨域访问接口数据 一、创建SpringBoot 文件 创建一个 SpringBoot 文件&#xff0c;文件结构目录如下&#xff1a; 二、在pom.xml添加依赖 <!--Spring Boot 依赖--> <parent><artifactId>sp…

WireShark下载安装

下载地址 WireShark站内下载资源&#xff1a;&#xff08;土豪方便下载&#xff09; https://download.csdn.net/download/qq_58662768/89377088 官网下载&#xff1a; Wireshark Go Deep 进入主页后&#xff0c;选择Get Acquainted&#xff0c;再选择Download。 选择合适…

TPL0401B使用教程

1.前言 前面做程控放大器的时候&#xff0c;有除开AD602&#xff0c;还有一个AD620&#xff0c;性能更好&#xff0c;不过是通过外部电阻来控制放大倍数的&#xff0c;不过要是接滑动变阻器就太不优雅了&#xff0c;而且单片机怎么控制滑动变阻器&#xff1f;&#xff08;难不…

linux 内核哪种锁可以递归调用 ?

当数据被多线程并发访问(读/写)时&#xff0c;需要对数据加锁。linux 内核中常用的锁有两类&#xff1a;自旋锁和互斥体。在使用锁的时候&#xff0c;最常见的 bug 是死锁问题&#xff0c;死锁问题很多时候比较难定位&#xff0c;并且影响较大。本文先会介绍两种引起死锁的原因…