[Linux进程(一)] 什么是进程?PCB的底层是什么?以及进程标识符pid与ppid

在这里插入图片描述

文章目录

  • 1、前言
  • 2、描述进程 — PCB(os怎么管理进程呢)
  • 3、查看进程
    • 3.1 方法一
    • 3.2 方法二
  • 4、系统调用获取进程标示符(PID)
    • 4.1 获取进程的ID
    • 4.2 获取进程的父进程ID
  • 5、系统调用创建子进程-fork

1、前言

大家经常都在讲进程,而它到底是什么呢?
这里给大家先简单的介绍一下:

  • 课本概念:程序的一个执行实例,正在执行的程序等。
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。

2、描述进程 — PCB(os怎么管理进程呢)

在操作系统中我们说到,os管理的本质并不是对事物本体的管理,而是对数据的管理。
这里虽然我们并不知道进程是什么,但是按照上面的理解,我们对进程的管理,也是对进程的数据进行管理,这里就用到那六个字“先描述,再组织”。我们来画图理解一下:
在这里插入图片描述
操作系统可能会同时管理非常多的“进程”,因此在管理的时候,管理这些进程的数据即可,将这些进程共有的属性提取出来,用结构体将其存起来,再使用链表将其链接起来,这样就能有效的管理进程,这样的机制就叫做“先描述,再组织”。
这个进程属性的集合就叫做PCB,也叫做进程控制块。
PCB是一个统称,在Linux中,PCB叫做 task_struct。
最终,我们操作系统对PCB的管理就被建模成了对链表的增删查改。
我们得出:进程 = 可执行程序 + 内核数据结构

3、查看进程

3.1 方法一

在Linux下我们可以使用下面的这个命令来查看进程:

ps ajx

在这里插入图片描述
这是所有的进程,如果我们只是对其中一个进程查看呢?我们看下面的:
我们先写一段C语言程序,编译为可执行程序并执行,然后使用该命令来查看一下进程:

#include <stdio.h>
#include <unistd.h>
 
int main()
{
    while(1)
    {
        printf("我是一个进程\n");
        sleep(1);
    }
 
    return 0;
}

在这里插入图片描述
命令:

ps ajx | grep mycode

这里用到我们以前学到的 管道符与过滤,筛选出我们想找的进程。

3.2 方法二

我们可以通过根目录下的proc目录来查看。proc目录下保存有进程的信息。
在这里插入图片描述

proc目录下的蓝色数字就是每一个进程的pid(Process ID),这就跟我们的身份证号一样,一人只有一个,每个进程都只有这么一个标识符。
我们再次以刚才那段C语言为例,来查看进程:
在这里插入图片描述
在这里插入图片描述
如果我们删掉磁盘上可执行程序,进程没有终止的话,我们仍然是可以查到的,因为进程是程序拷贝到内存中的,代码+数据都被拷贝下来了,所以删掉磁盘上的可执行程序不会影响的。
在这里插入图片描述
当终止掉程序,内存中拷贝的可执行程序与数据都被释放掉了,因此再去查就查不出来了。
在这里插入图片描述

4、系统调用获取进程标示符(PID)

  • 进程id(PID)
  • 父进程id(PPID)
    我们在上面使用 ps ajx 命令的时候发现,有一个PID,还有一个PPID,我们不难猜到,这些都是PCB中的属性。
    如果获取呢?这里要介绍我们第一个要学习系统调用,getpid与getppid了。我们使用man手册查一下看看怎么说。
    在这里插入图片描述
    我们接下来就使用getpid与getppid系统调用接口来看看能不能查到pid与ppid。

4.1 获取进程的ID

这里我们写一段C语言程序来调用看看:

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

编译后我们运行可执行程序,并使用下面 ps ajx 命令来对比看,pid是否正确。

ps ajx | head -1 && ps ajx | grep mycode | grep -v grep

在这里插入图片描述
我们发现,getpid确实是没错的,并且我们调用是没有出错的。
当我们想要终止掉一个进程的时候,我们总是用 ctrl + c,今天我们再来学一个终止命令,kill 命令。

kill -9 pid  // 这里的9是9号信号

在这里插入图片描述

4.2 获取进程的父进程ID

我们继续改写上面的C语言代码,再来看看 getppid() 系统调用接口。

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

在这里插入图片描述
经过我的不断调用发现,每次运行pid都是在变化的,但是ppid确实一直没有变化,我猜它是一直在运行的,我们来查看一下它是谁。
在这里插入图片描述
原来它是命令行解释器 bash 呀。我们由此也知道了,我们所敲出来的命令都是 bash 的子进程。
那我们Linu下有没有创建子进程的系统接口呢,有,我们再来学一下。

5、系统调用创建子进程-fork

我们可以使用fork创建子进程,先查一下man手册看看:
在这里插入图片描述
我们写一份 C语言代码来试着调用系统接口 fork()。
我们先想一下,我们有自己的进程为什么还要去创建子进程呢?
在上面的学习我们看到,我们输入的命令是执行命令得到结果,而父进程bash是解释你所输入的命令,这就说明,我们父子进程各自做各自的事。由此,我们想是不是可以,按照fork的返回值配合 if-else 语句来执行不同的事。我们试一下:

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("我是一个父进程,我的pid:%d\n", getpid());
     
    pid_t id = fork();
    // fork之后用if分流
    if(id < 0) return -1;
    else if(id == 0)
    {
        // child
        while(1)
        {                
            printf("我是子进程,pid:%d, ppid:%d\n", getpid(), getppid());
            sleep(1);                                                             
        }
    }
    else 
    {
        // parent
    	while(1)
        {
            printf("我是父进程,pid:%d, ppid:%d\n", getpid(), getppid());
            sleep(1);
        }
	}

    return 0;
}

我们编译后运行可执行程序。
在这里插入图片描述

通过打印出来的结果,我们发现刚我们的猜测是正确的!!!
这里我们可以总结出几个问题,对fork作以总结:
1、fork干了什么事情?
调用fork()后,创建了子进程,父子进程可以协作,干不同的事。
2、为什么fork会有两个返回值?
我们在查文档的时候,就有返回值的介绍。
成功时,给父进程返回子进程的pid,给子进程返回0;失败时,给父进程返回-1,不创建子进程。
fork之后代码共享,fork函数执行两次就有两个返回值,一次返回父进程接收,另外一次返回子进程接收。
3、为什么fork的两个返回值,会给父进程返回子进程pid,给子进程返回0?
我们与现实生活联系起来。现实中,父 : 子 = 1 : n(n >= 1),父亲给孩子不同名字,可以更好的区分孩子(保证唯一性)。这样类比到这里,给父进程返回子进程的pid,父进程必须有标识子进程的方式,而子进程只需要知道自己是否被创建成功,以及知道父进程是谁即可 (调用 getppid())。
4、fork之后谁先运行?
我们上面运行了可执行程序后,发现先跑了父进程的代码片段,然后跑子进程的代码片段,但是后面并不是这样的规律。
当创建完子进程后,系统的其他进程,以及父进程和子进程都要被调度。父子进程的PCB都被创建并在运行队列中排队,哪个进程的PCB先被调度,哪个进程就先被运行,是由操作系统决定的,不确定的。
5、如何理解同一个id变量,怎么会有不同的值?
当父子进程都被运行时,他们共用一份代码与数据,但是一旦发生数据的写入,就会发生写时拷贝,数据就不再共享,而是父子进程各一份自己的数据,因此就会出现同一个变量,值不相同。

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

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

相关文章

UE5 UE4 修复GPU驱动程序崩溃

原贴链接&#xff1a;https://mp.weixin.qq.com/s/e5l9XtfwEFWgwhHi1b2idg UE5 UE4在处理含有大量图形的项目时&#xff0c;你有可能会遇到GPU崩溃 可以通过修改注册表&#xff0c;修复崩溃。 GPU崩溃情况概述 UE5 UE4在处理含有大量图形的项目时&#xff0c;你有可能会遇到G…

二线厂商-线上测评-大数据开发

曾经投递过一些中级岗位&#xff0c;在面试之前&#xff0c;会通过邮件的方式把性格测试的题目发给你让你做一下。 一般分为单选题&#xff0c;多选题&#xff0c;性格测试题&#xff0c;认知理解题等等。 大概做了一个小时吧。 单选题&#xff1a; 感觉就是类似于以前高中时候…

AI芯片:神经网络研发加速器、神经网络压缩简化、通用芯片 CPU 加速、专用芯片 GPU 加速

AI芯片&#xff1a; 神经网络研发加速器、神经网络压缩简化、通用芯片 CPU 加速、专用芯片 GPU 加速 神经网络研发加速器神经网络编译器神经网络编译器 神经网络加速与压缩&#xff08;算法层面&#xff09;知识蒸馏低秩分解轻量化网络剪枝量化 通用芯片 CPU 加速x86 加速arm 加…

OceanBase原生分布式数据库

1.历史背景 在Java Web项目中&#xff0c;常常使用免费开源的MySQL数据库存储业务数据&#xff0c;按业界经验MySQL单库超过多大数据体量&#xff0c;或单表超过几百万条数据后就会出现查询变慢的情况&#xff0c;单实例数据库只能扩展物理资源(CPU、内存)&#xff0c;来提升查…

ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现

阅读本文你的收获 了解JWT身份认证的流程了解基于JWT身份认证和Session身份认证的区别学习如何在ASP.NET Core WebAPI项目中封装JWT认证功能 在上文ASP.NET Core高级之认证与授权(一)–JWT入门-颁发、验证令牌中演示了JWT认证的一个入门案例&#xff0c;本文是一个基于JWT认证…

docker 安装redis (亲测有效)

目录 1 安装 1 安装 1 将redis 的 tar 包 上传到服务器 上传之后tar 包&#xff0c;将他变成镜像 输入docker images,发现目前是没有镜像的&#xff0c;现在将tar 包变成镜像 docker load -i redis.tar以上就将tar 包变成镜像了 现在在宿主机找一个地方&#xff0c;存放数据…

跟我学java|Stream流式编程——并行流

什么是并行流 并行流是 Java 8 Stream API 中的一个特性。它可以将一个流的操作在多个线程上并行执行&#xff0c;以提高处理大量数据时的性能。 在传统的顺序流中&#xff0c;所有的操作都是在单个线程上按照顺序执行的。而并行流则会将流的元素分成多个小块&#xff0c;并在多…

DHSP和DNS

一、服务程序 1.1DHCP定义 DHCP&#xff08;动态主机配置协议&#xff09;是一个局域网的网络协议。指的是由服务器控制一段IP地址范围&#xff0c;客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码。默认情况下&#xff0c;DHCP作为Windows Server的一个服务组…

java基础之Java8新特性-方法引入

目录 1.简介 2.方法引入 方法引入遵循规范 方法引入种类 1.静态方法引入 2.对象方法引入 3.实例方法引入 4.构造函数引入 1.简介 方法引用是 Java 8 中引入的另一个重要特性&#xff0c;它提供了一种简洁的语法来直接引用现有方法或构造函数。方法引用可以看作是 Lambd…

复试 || 就业day14(2024.01.10)算法篇

文章目录 前言字符串中第二大的数字字符串中不同整数的数目判断句子是否为全字母句长度为三且各字符不同的子字符串检查是否区域内所有整数都被覆盖*重新分配字符使所有字符串都相等可以输入的最大单词数检查是否所有字符出现次数相同差的绝对值为 K 的数对数目至少在两个数组中…

设计模式之外观模式【结构型模式】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档> 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某…

数组计算模块NumPy

NumPy是Python数组计算、矩阵运算和科学计算的核心库。 提供了高性能的数组对象提供了大量的函数和方法NumPy使用机器学习中的操作变得简单NumPy是通过C语言实现的 NumPy的安装 pip install numpy 数组的分类 一维数组 跟Python列表的形状一样&#xff0c;区别在于数组的…

MongoDB—SQL到MongoDB映射图表

一、术语和概念 下表显示了各种 SQL 术语和概念 以及相应的 MongoDB 术语和概念。 SQL Terms/Concepts MongoDB Terms/Concepts database database table collection row document or BSON document column field index index table joins $lookup, embedded docu…

RV1126边缘计算AI盒子,支持4-6路1080p视频,2T 算力

1 产品概述 信迈推出基于瑞芯微Rockchip RV1126架构的AI边缘计算主板&#xff0c;RV1126芯片是四核ARM Cortex-A7,1.5GHz&#xff0c; RSIC-V 200MHz CPU &#xff0c;NPU2.0Tops。AI边缘计算主板外围接口丰富&#xff0c;拥有超强扩展性&#xff0c;可广泛应用在智慧安防、工…

贝锐蒲公英云智慧组网解读:实现工业设备远程调试、异地PLC互联

这个时候&#xff0c;使用异地组网是非常有效的解决方案。在12月28日贝锐官方的直播中&#xff0c;请到了贝锐蒲公英的技术研发经理&#xff0c;为大家分享了贝锐蒲公英云智慧组网解决方案&#xff0c;以及蒲公英二层组网相关的技术和应用。 搜索“贝锐”官方视频号&#xff0c…

AI数据和测试评价

1数据收集和清洗 为了训练AI模型,需要收集和准备足够的数据。数据应该涵盖各种情况和场景,以确保系统在各种情况下都能准确地运行。数据原始来源应该是真实的,并且应该涵盖系统预计的使用情况。数据应该根据特定的需求进行采样和处理,可以来自各种来源,例如公共数据集、第…

Oracle数据库19c OCP 1z0-082考场真题解析第24题

考试科目&#xff1a;1Z0-082 考试题量&#xff1a;90 通过分数&#xff1a;60% 考试时间&#xff1a;150min 本文为云贝教育郭一军guoyJoe原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。【云贝教育】Orac…

Matlab 之数据分布拟合

文章目录 Part.I IntroductionPart.II Distribution Fitter APP 的使用Chap.I APP 简介Chap.II 简单使用 Part.III 通过代码实现分布拟合Chap.I 基于 fitdist 函数Chap.II 获取数据的频率分布后进行曲线拟合 Reference Part.I Introduction 本文主要介绍了如何使用 Matlab 对数…

【Verilog】期末复习——分别画出下面两个程序综合后的电路图/reg型数据和wire型数据的区别

系列文章 数值&#xff08;整数&#xff0c;实数&#xff0c;字符串&#xff09;与数据类型&#xff08;wire、reg、mem、parameter&#xff09; 运算符 数据流建模 行为级建模 结构化建模 组合电路的设计和时序电路的设计 有限状态机的定义和分类 期末复习——数字逻辑电路分…

设计模式-空对象模式

设计模式专栏 模式介绍模式特点应用场景空对象模式和单例模式的区别代码示例Java实现空对象模式Python实现空对象模式 空对象模式在spring中的应用 模式介绍 空对象模式是一种设计模式&#xff0c;用于处理对象不存在的情况。它通过返回一个空对象来代替 null 值&#xff0c;从…