[Linux]进程概念

[Linux]进程概念

文章目录

  • [Linux]进程概念
    • 进程的定义
    • 进程和程序的关系
    • Linux下查看进程
    • Linux下通过系统调用获取进程标示符
    • Linux下通过系统调用创建进程-fork函数使用

进程的定义

进程是程序的一个执行实例,是担当分配系统资源(CPU时间,内存)的实体。

进程和程序的关系

由编程语言编写的代码经过编译后形成的二进制程序会存储在硬盘中,当计算机启动一个程序时,会将程序的相关代码和和数据加载到内存中,供CPU来使用:

image-20230803184610548

程序的代码和数据加载到内存后,操作系统就要对程序进行管理,为了更好的管理这些程序,需要对先创建相应的结构来描述这些程序,在操作系统中,用于描述程序的结构叫做进程控制块(Process Control Block,简称 PCB),Linux系统下的PCB名为task_struct,PCB中也会记录相应的代码和数据的地址,为了更好的访问这些PCB使用链式结构将其组织起来:

image-20230803185254037

task_ struct内容分类如下:

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息。

如果只是将程序的代码和数据加载到内存中,但是操作系统没有为其创建PCB进行管理,操作系统就不会调度它,它就无法完成程序的执行,因此进程的本质是内存中的代码和数据+进程控制块,有了PCB后,操作系统就将对进程的管理转化为了对PCB的管理,比如如果要关闭一个进程就将其PCB删除,然后对应的内存就会清空其在内存中的代码和数据:

image-20230803190524985

Linux下查看进程

为了更好的在Linux操作系统上查看进程,创建源文件myprocess.c和makefile文件来创建二进制程序,

源文件中内容如下:

#include <stdio.h>
#include <unistd.h>int main()
{while(1){printf("hello myprocess\n");sleep(1);}return 0;
}

其中makefile的内容如下:

myprocess:myprocess.cgcc -o myprocess myprocess.c
.PHONY:clean
clean:rm -f myprocess

创建好以上文件并编译得到名为myprocess的二进制程序,然后在Linux下启动两个客户端,其中一个启动程序变成进程:

image-20230803193450976

再另一个客户端输入ps axj | head -1 && ps axj | grep myprocess | grep -v grep查看myprocess进程:

image-20230803193707725

以上为使用指令查看进程,指令如下:

ps axj | head -1 && ps axj | grep 进程名 | grep -v 进程名

另外还可以在/proc目录下看到进程:

image-20230803193931708

/proc目录是一个内存级的目录,不存在于硬盘中,目录中会有命名和pid相同的目录,该目录中会记录对应进程的task_struct,如果进程关闭了对应的目录也就删除了。

Linux下通过系统调用获取进程标示符

Linux操作系统为了唯一标识一个进程,给每个进程设置了一个进程标识符在PCB中,也就是pid。并且也提供了系统接口函数getpid来获取当前进程的pid,其介绍如下:

image-20230803195336806

为了测试getpid函数修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){printf("hello myprocess, 我的pid是%d\n", getpid());sleep(1);}return 0;
}

用指令查询进程pid和查看进程执行结果:

image-20230803195628555

另外Linux操作系统中还设置了父进程标识符,用于记录当前进程的父进程pid,也就是ppid,同时也提供了getppid函数来获取当前进程的ppid,ppid的介绍如下:

image-20230803200016344

为了测试getppid函数修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){printf("hello myprocess, 我的pid是%d, 我的ppid为%d\n", getpid(), getppid());sleep(1);}return 0;
}

用指令查询进程pid和查看进程执行结果:

image-20230803201344196

多次利用 ctrl+c关闭进程,然后重新启动进程:

image-20230803201448298

可以看出,无论进程的pid如何变化,进程的ppid都不会变化,我们尝试用指令查看这个父进程:

image-20230803201620342

实际上这个这个父进程就是bash,通过如上现象我们可以得到如下结论:

  • 命令行解释器(bash)本质也是一个进程。
  • 命令行启动的所有程序最终都会变成进程,而该进程对应的父进程都是bash。

Linux下通过系统调用创建进程-fork函数使用

fork函数是Linux系统提供的创建子进程的系统调用。

  • fork函数运行成功后,执行流会变成两个,一个是调用fork函数的父进程,另一个是fork函数创建的子进程。
  • 创建的子进程会和父进程共享父进程代码和数据,子进程会执行父进程fork函数创建子进程之后的代码。
  • fork函数给父进程返回子进程的pid,给创建的子进程返回0,出错返回-1。

为了测试fork函数修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>int main()
{pid_t id = fork();if (id == 0){//子进程printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());sleep(2);}else if (id > 0){//父进程printf("我是父进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());sleep(3);}else {//fork函数出错assert(1);}return 0;
}

说明:

  • fork函数所需要的头文件是unistd.h
  • 使用条件判断来控制父子进程执行不同的代码。

用指令查询进程和查看进程执行结果:

image-20230804105943957

fork函数的原理

进程的本质是PCB+内存中的代码和数据,由于fork函数创建的子进程是和父进程共享代码和数据的,因此fork函数创建子进程的原理是创建一个PCB给子进程,该PCB中大部分数据是和父进程相同的,并且指向同一份代码和数据:

image-20230804113115080

进程独立性在fork中的体现

首先给出如下定理:进程之间是相互独立的,一个进程的任何操作都不会影响其他进程。

在使用fork函数创建子进程进程之间的独立性也能得到保证,为了验证独立性修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>int main()
{pid_t id = fork();if (id == 0){//子进程printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());sleep(20);printf("我是子进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());printf("我是子进程,我已经关闭了\n");}else if (id > 0){//父进程printf("我是父进程,我的pid是%d, 我的ppid是%d\n", getpid(), getppid());sleep(3);printf("我是父进程,我已经关闭了\n");}else {//fork函数出错assert(1);}return 0;
}

用指令查询进程和查看进程执行结果:

开始时,父子进程一起执行:

image-20230804114253740

父进程关闭,子进程正常运行:

image-20230804114347222

最后子进程关闭:

image-20230804114411668

由以上测试可以看出,父进程的关闭不影响子进程正常执行,保证了一定的独立性。另外由于代码是只读的,父进程无法通过修改代码来影响子进程,而数据的修改会触发写时拷贝机制,保证了一定的独立性。

为了观察写时拷贝现象,修改源文件myprocess.c,内容如下:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>int main()
{int a = 0;pid_t id = fork();if (id == 0){//子进程printf("我是子进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);sleep(5);printf("我是子进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);}else if (id > 0){//父进程printf("我是父进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);a = 666;printf("我是父进程,我的pid是%d, 我的ppid是%d, a:%d, &a:%p\n", getpid(), getppid(), a, &a);sleep(3);printf("我是父进程,我已经关闭\n");}else {//fork函数出错assert(1);}return 0;
}

查看进程执行结果:

image-20230804120919703

观察现象可以发现,父进程修改a的值后,子进程的a的值并没有改变,但是父进程和子进程的a变量的地址是相同,这就是发生了写时拷贝造成的现象。

fork函数返回两个返回值的原理

由于fork创建的子进程和父进程共享代码和数据,并且fork函数也是父进程的代码的一部分,因此父进程完成子进程的创建后,子进程也会执行fork函数创建子进程后续的剩余代码,其中就包括fork函数中return返回的部分,因此父进程执行了return部分,子进程也执行了return部分,造成fork函数返回两个返回值的现象:

image-20230804121340511

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统架构师---软件重用、基于架构的软件设计、软件模型

目录 软件重用 构件技术 基于架构的软件设计 ABSD方法与生命周期 抽象功能需求 用例 抽象的质量和业务需求 架构选项 质量场景 约束 基于架构的软件开发模型 架构需求 需求获取 标识构件 需求评审 架构设计 架构文档 架构复审 架构实现 架构演化 前言&…

0009Java程序设计-jsp在线学习平台设计与实现

摘 要目 录系统实现开发环境 摘 要 在线学习平台&#xff0c;是一个利用因特网作为平台传送教学内容&#xff0c;实施网上教学&#xff0c;进行网上交流和学习的信息系统。构建在线学习系统平台&#xff0c;可以克服传统课堂教育的局限性&#xff0c;形成一种主动的、协作的、…

一个新的品牌如何快速做好品牌宣传?媒介盒子有绝招

互联网快速发展的今天&#xff0c;大量信息进入人们的生活&#xff0c;只要有流量就将成为广告的渠道。今天这里提到的是新品牌&#xff0c;相比较而言又具有一定的特殊性。 新品牌可能是一个创业公司&#xff0c;刚刚研发出来的品牌&#xff0c;想要冲进这个信息化的市场&…

基于swing的图书借阅管理系统java jsp书馆书籍信息mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的图书借阅管理系统 系统有2权限&#xff1…

60.每日一练:回文数(力扣)

目录 问题描述 代码解决以及思想 解法&#xff08;一&#xff09; 知识点 解法&#xff08;二&#xff09; 问题描述 代码解决以及思想 解法&#xff08;一&#xff09; class Solution { public:bool isPalindrome(int x) {string arr to_string(x); // 将整数转换为…

[MyBatis系列③]动态SQL

目录 1、简介 2、if标签 3、foreach标签 4、SQL抽取 ⭐MyBatis系列①&#xff1a;增删改查 ⭐MyBatis系列②&#xff1a;两种Dao开发方式 1、简介 开发中在MyBatis映射文件配置SQL语句&#xff0c;但是前面配置的都是比较简单的&#xff0c;不涉及稍复杂的业务场景。想要应…

【C# Programming】编程入门:数组、操作符、控制流

目录 一、数组 1、数组的声明 1.1 一维数组声明&#xff1a; 1.2 多维数组声明&#xff1a; 2、数组的实例化和赋值 2.1 数组在声明时通过在花括号中使用以逗号分隔的数据项对数组赋值&#xff0c; 例如&#xff1a; 2.2 如果在声明后赋值&#xff0c;则需…

MySQL高级篇——MySQL架构篇2(MySQL的数据目录)

目录 1 MySQL8的主要目录结构1.1 数据库文件的存放路径1.2 相关命令目录1.3 配置文件目录 2 数据库和文件系统的关系2.1 查看默认数据库2.2 数据库在文件系统中的表示2.3.1 InnoDB存储引擎模式2.3.2 MyISAM存储引擎模式 2.4 总结2.5 视图在文件系统中的表示2.6 其他的文件 1 My…

双指针算法实例3(快乐数)

题目&#xff1a; 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结…

VMware Workstation搭建Centos7虚拟机详细步骤

直接按照图文步骤进行操作即可。 目录 1、新建虚拟机 2、典型安装 3、稍后安装操作系统 4、版本选择CentOS 7 64位 5、设置虚拟机的名称和位置 6、设置磁盘大小 7、虚拟机向导任务完成 8、虚拟机设置 9、开启虚拟机 10、正式安装虚拟机第一步 11、选择安装时的语言 …

python中的matplotlib画散点图(数据分析与可视化)

python中的matplotlib画散点图&#xff08;数据分析与可视化&#xff09; import numpy as np import pandas as pd import matplotlib.pyplot as pltpd.set_option("max_columns",None) plt.rcParams[font.sans-serif][SimHei] plt.rcParams[axes.unicode_minus]Fa…

基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 2 Inport和Outports 标签页介绍

上篇我们介绍了Function页的内容,这篇我们介绍Inports和Outports页的内容,这里我们再次强调一个概念,code mapping是以simulink的角度去看的,就是先要在模型中建立simulink模块,在code mapping里映射他要对应的autosar的元素,之后生成代码时的c语言的名字是以Autosar的元…