[Linux]进程概念
[Linux]进程概念
文章目录
- [Linux]进程概念
- 进程的定义
- 进程和程序的关系
- Linux下查看进程
- Linux下通过系统调用获取进程标示符
- Linux下通过系统调用创建进程-fork函数使用
进程的定义
进程是程序的一个执行实例,是担当分配系统资源(CPU时间,内存)的实体。
进程和程序的关系
由编程语言编写的代码经过编译后形成的二进制程序会存储在硬盘中,当计算机启动一个程序时,会将程序的相关代码和和数据加载到内存中,供CPU来使用:
程序的代码和数据加载到内存后,操作系统就要对程序进行管理,为了更好的管理这些程序,需要对先创建相应的结构来描述这些程序,在操作系统中,用于描述程序的结构叫做进程控制块(Process Control Block,简称 PCB),Linux系统下的PCB名为task_struct,PCB中也会记录相应的代码和数据的地址,为了更好的访问这些PCB使用链式结构将其组织起来:
task_ struct内容分类如下:
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息。
如果只是将程序的代码和数据加载到内存中,但是操作系统没有为其创建PCB进行管理,操作系统就不会调度它,它就无法完成程序的执行,因此进程的本质是内存中的代码和数据+进程控制块,有了PCB后,操作系统就将对进程的管理转化为了对PCB的管理,比如如果要关闭一个进程就将其PCB删除,然后对应的内存就会清空其在内存中的代码和数据:
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下启动两个客户端,其中一个启动程序变成进程:
再另一个客户端输入ps axj | head -1 && ps axj | grep myprocess | grep -v grep
查看myprocess进程:
以上为使用指令查看进程,指令如下:
ps axj | head -1 && ps axj | grep 进程名 | grep -v 进程名
另外还可以在/proc目录下看到进程:
/proc目录是一个内存级的目录,不存在于硬盘中,目录中会有命名和pid相同的目录,该目录中会记录对应进程的task_struct,如果进程关闭了对应的目录也就删除了。
Linux下通过系统调用获取进程标示符
Linux操作系统为了唯一标识一个进程,给每个进程设置了一个进程标识符在PCB中,也就是pid。并且也提供了系统接口函数getpid来获取当前进程的pid,其介绍如下:
为了测试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和查看进程执行结果:
另外Linux操作系统中还设置了父进程标识符,用于记录当前进程的父进程pid,也就是ppid,同时也提供了getppid函数来获取当前进程的ppid,ppid的介绍如下:
为了测试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和查看进程执行结果:
多次利用 ctrl+c
关闭进程,然后重新启动进程:
可以看出,无论进程的pid如何变化,进程的ppid都不会变化,我们尝试用指令查看这个父进程:
实际上这个这个父进程就是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
。 - 使用条件判断来控制父子进程执行不同的代码。
用指令查询进程和查看进程执行结果:
fork函数的原理
进程的本质是PCB+内存中的代码和数据,由于fork函数创建的子进程是和父进程共享代码和数据的,因此fork函数创建子进程的原理是创建一个PCB给子进程,该PCB中大部分数据是和父进程相同的,并且指向同一份代码和数据:
进程独立性在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;
}
用指令查询进程和查看进程执行结果:
开始时,父子进程一起执行:
父进程关闭,子进程正常运行:
最后子进程关闭:
由以上测试可以看出,父进程的关闭不影响子进程正常执行,保证了一定的独立性。另外由于代码是只读的,父进程无法通过修改代码来影响子进程,而数据的修改会触发写时拷贝机制,保证了一定的独立性。
为了观察写时拷贝现象,修改源文件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;
}
查看进程执行结果:
观察现象可以发现,父进程修改a的值后,子进程的a的值并没有改变,但是父进程和子进程的a变量的地址是相同,这就是发生了写时拷贝造成的现象。
fork函数返回两个返回值的原理
由于fork创建的子进程和父进程共享代码和数据,并且fork函数也是父进程的代码的一部分,因此父进程完成子进程的创建后,子进程也会执行fork函数创建子进程后续的剩余代码,其中就包括fork函数中return返回的部分,因此父进程执行了return部分,子进程也执行了return部分,造成fork函数返回两个返回值的现象:
相关文章:

[Linux]进程概念
[Linux]进程概念 文章目录 [Linux]进程概念进程的定义进程和程序的关系Linux下查看进程Linux下通过系统调用获取进程标示符Linux下通过系统调用创建进程-fork函数使用 进程的定义 进程是程序的一个执行实例,是担当分配系统资源(CPU时间,内存…...
GEE/PIE遥感大数据处理与应用
随着航空、航天、近地空间等多个遥感平台的不断发展,近年来遥感技术突飞猛进。由此,遥感数据的空间、时间、光谱分辨率不断提高,数据量也大幅增长,使其越来越具有大数据特征。对于相关研究而言,遥感大数据的出现为其提…...
● 647. 回文子串 ● 516.最长回文子序列
647. 回文子串 class Solution { public:int countSubstrings(string s) {vector<vector<bool>>dp(s.size(),vector<bool>(s.size(),false));int res0;for(int is.size()-1;i>0;i--){for(int ji;j<s.size();j){if(s[i]s[j]){if(j-i<1){res;dp[i][…...

Mysql group by使用示例
文章目录 1. groupby时不能查询*2. 查询出的列必须在group by的条件列中3. group by多个字段,这些字段都有索引也会索引失效,只有group by单个字段索引才能起作用4. having条件必须跟group by相关联5. 用group by做去重6. 使用聚合函数做数量统计7. havi…...

淘宝商品详情采集接口item_get-获得淘宝商品详情(可高并发线程)
获得淘宝商品详情页面数据采集如下: taobao.item_get 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)注册key账号接入secretString是调用密钥api_nameString是API接口名称(包括在请求地址中࿰…...
uniapp写公众号h5开发 附件上传 下载功能
一。 uni-app实现文件上传功能 目前,找到一款第三方插件 文件上传插件地址 https://ext.dcloud.net.cn/plugin?id=1015 将插件下载并导入项目中直接拿来使用,插件市场也有对改插件用法的描述。 用法: 1. 以下代码写于根目录下第一个view顶部或跟在自定义导航栏后面 // 以…...
机器学习基础09-审查分类算法(基于印第安糖尿病Pima Indians数据集)
算法审查是选择合适的机器学习算法的主要方法之一。审查算法前并 不知道哪个算法对问题最有效,必须设计一定的实验进行验证,以找到对问题最有效的算法。本章将学习通过 scikit-learn来审查六种机器学习的分类算法,通过比较算法评估矩阵的结果…...
C++ sort与优先队列排序的区别
int main() {vector<int> data{3, 1, 2};cout << "从小到大排序" << endl;sort(data.begin(), data.end(), std::less<int>());printContainer(data);auto cmp1 [](int x, int y) { return x < y; };sort(data.begin(), data.end(), cmp…...

【Rust】Rust学习 第十九章高级特征
现在我们已经学习了 Rust 编程语言中最常用的部分。在第二十章开始另一个新项目之前,让我们聊聊一些总有一天你会遇上的部分内容。你可以将本章作为不经意间遇到未知的内容时的参考。本章将要学习的功能在一些非常特定的场景下很有用处。虽然很少会碰到它们…...
C++ 纯虚函数和虚函数的区别
在 C 中,虚函数(Virtual Function)和纯虚函数(Pure Virtual Function)都是用于实现多态性的机制,但它们之间有一些关键的不同。 虚函数(Virtual Function) 定义:在基类…...

Go中的有限状态机FSM的详细介绍 _
1、FSM简介 1.1 有限状态机的定义 有限状态机(Finite State Machine,FSM)是一种数学模型,用于描述系统在不同状态下的行为和转移条件。 状态机有三个组成部分:状态(State)、事件(…...

Python入门教程 | Python3 基本数据类型
赋值 Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象的类型。 等号(ÿ…...
STM32移植u8g2玩转oled 用软件iic实现驱动oled
移植u8g2到stm int fputc(int ch,FILE *f) {ITM_SendChar(ch);return (ch); }void delay_us(uint32_t time) {uint32_t i8*time;while(i--); }uint8_t STM32_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {//printf("%s:msg %d,arg_int …...
C++ 学习系列 -- string 实现
string是C标准库的重要部分,主要用于字符串处理。这里我们自己实现一个简单版本的 string. 一 思路 string 类中应该包含如下: 1. 类成员变量:char* m_data,利用 char* 指针存放字符串 2. 成员函数: 2.1 size(…...

C语言小练习(三)
🌞 “也许你感觉自己与周遭格格不入,但正是那些你一人度过的时光,让你变得越来越有意思,等有天别人终于注意到你的时候,他们就会发现一个比他们想象中更酷的人。”-《生活大爆炸》 Day03 📝 一.选择题&…...
2023 js逆向爬虫 有道翻译 代码
前置条件:nodejs环境、安装 crypto 和 python3环境 js.js文件: const crypto require("crypto")function decode(resp_data) {g_o ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHlg_n ydsecre…...

【物联网无线通信技术】NFC从理论到实践(FM17XX)
NFC,全称是Near Field Communication,即“近场通信”,也叫“近距离无线通信”。NFC诞生于2004年,是基于RFID非接触式射频识别技术演变而来,由当时的龙头企业NXP(原飞利浦半导体)、诺基亚以及索尼联合发起。NFC采用13.5…...

Python爬虫猿人学逆向系列——第六题
题目:采集全部5页的彩票数据,计算全部中奖的总金额(包含一、二、三等奖) 地址:https://match.yuanrenxue.cn/match/6 本题比较简单,只是容易踩坑。话不多说请看分析。 两个参数,一个m一个f&…...

idea使用tomcat
1. 建立javaweb项目 2. /WEB-INF/web.xml项目配置文件 如果javaweb项目 先建立项目,然后在项目上添加框架支持,选择javaee 3. 项目结构 4.执行测试:...

搭建Tomcat HTTP服务:在Windows上实现外网远程访问的详细配置与设置教程
文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器,不仅名字很有趣࿰…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...