当前位置: 首页 > news >正文

Linux:进程概念详解

进程概念详解

一、进程的基本概念

进程在书本上的定义是:计算机中正在运行的程序实例。仅此描述可能让很多人感到困惑。

我们磁盘上存储着.exe文件,启动文件时,文件会从磁盘加载到内存,由CPU对文件的数据和代码进行运算。但进程并非仅仅是数据和代码。

 在计算机中有许许多多个应用程序在指向,有些程序可能是新执行进来的,有些程序可能是执行完了将要结束,可能还有些程序需要阻塞等待。有这么多种程序也有这么多种情况,那么操作系统是需要对这些程序进行管理,但又应该怎么管理呢?

计算机中有众多应用程序,它们的执行情况各不相同,操作系统需要对这些程序进行管理。每个程序都有各种属性,如代码地址、数据地址和最重要的ID(用来标识进程的唯一标识符)。操作系统将这些属性通过struct整合,用来描述一个应用程序当前的状态,并且每个struct都有一个next指针指向下一个整合属性,从而将对进程的管理转化为对链表的管理。我们把每一个struct都称之为内核数据结构对

因此,进程 = 内核数据结构对象 + 数据和代码。

二、认识进程

  进程的计算机业里统称为Process Control Block(PCB).

PCB通常包含以下内容

  • 进程状态:如就绪、运行、阻塞等。
  • 进程ID:用来表示进程唯一性的标识符。
  • 程序计数器:指示进程下一条指令的地址。
  • CPU寄存器:保存进程在执行过程中使用的寄存器内容。
  • 内存管理信息:如进程的地址空间、页表等。
  • 调度信息:包括优先级、调度队列等信息。

在Linux中,PCB具体指的是task_struct,以下是其部分源代码

struct task_struct {

    volatile long state;  // 进程状态

    struct thread_info *thread_info;  // 指向线程信息的指针

    struct mm_struct *mm;  // 进程的内存描述符

    struct files_struct *files;  // 进程打开的文件描述符

    struct fs_struct *fs;  // 进程的文件系统信息

    struct signal_struct *signal;  // 信号处理相关

    struct sighand_struct *sighand;  // 信号处理程序

    struct task_struct *parent;  // 父进程

    struct list_head children;  // 子进程链表

    struct list_head sibling;  // 同级兄弟进程链表

    pid_t pid;  // 进程标识符

    pid_t tgid;  // 线程组标识符(线程的主线程)

    int exit_state;  // 进程退出状态

    unsigned long flags;  // 标记进程状态的标志位

    unsigned int prio;  // 进程优先级

    unsigned int static_prio;  // 静态优先级

    unsigned int normal_prio;  // 正常优先级

    unsigned int rt_priority;  // 实时优先级

    struct sched_entity se;  // 调度实体

    struct mm_struct *active_mm;  // 活动的内存描述符

    // 其他许多字段...

};

三、查看进程信息

(一)通过系统调用函数获取

getpid函数是一个系统调用函数,用于获取当前进程的进程ID(PID,Process ID)。

例如,我们可以在Linux中编写一个简单的死循环输出打印的程序,执行程序后,再打开一个Linux窗口,使用ps ajx | head -1 && ps axj |grep mycode命令显示当前系统中运行的进程信息。可以看到循环中输出的pid值与显示的pid值正好对应。

(二)通过/proc系统文件查看

我们还可以通过/proc系统文件查看进程信息。例如,使用ll /proc/9430 -1命令查看当前进程包含的内容,其中exe指的是当前进程对应的可执行文件,cwd表示当前进程运行所处的文件的绝对路径。这也解释了为什么通过fopen函数创建文件时,如果不带上绝对路径,文件就会在当前路径下创建。

此外,在使用ps ajx命令时,除了显示PID还有一个PPID,PPID是当前进程的父进程ID。我们可以通过调用getppid()函数查看当前进程的PPID。

通过上图运行可以看到,我们一直运行mycode的文件时,它的PID是依次递增的,但一直不变的是PPID。那mycode的PPID又是谁呢?

        通过上图运行可以看到,我们一直运行mycode的文件时,它的PID是依次递增的,但一直不变的是PPID。那mycode的PPID又是谁呢?

        通过ps ajx命令观察到,PID为8729的进程所代表的名称为 bash。

        那么 Bash 在Linux中是一个命令行解释器,用于解释用户输入的各种命令比如ls,pwd等。从中不难得出一个结论:我们历史上所执行的指令,工具,以及自己编写的程序,只要运行起来就都是进程。

四、创建子进程

在Linux中,有一个系统调用接口fork()用于创建子进程。

fork函数创建子进程后,子进程将继承父进程的代码,并且子进程将拥有一个唯一的进程ID。fork函数的返回值为pid_tpid_t其实是被typedef过的int

(一)fork()函数的使用示例

分析上图代码,一开始我们输出当前进程的PID和PPID,接着通过fork进行创建子进程后再次打印PID和PPID,我们会发现此时除了第一条打印的语句我们理解,还多了一条打印的语句。并且观察PID,会发现PID为 19215 的PPID正是一开始打印的PID 19214,那么就能够证明了fork会创建子进程,并且子进程会继承父进程fork()之后的代码,所以会打印两句输出语句。

调用fork()创建子进程后,返回值id会根据是父进程还是子进程进行区分:子进程返回的id为0,父进程返回的id是子进程的PID。

那这里可能就会产生一些疑惑。1.为什么fork给父进程返回的是子进程的PID,而子进程返回0。 2.为什么一个 fork() 函数会返回两次。 3.为什么一个id变量,即==0,又满足>0,if else if 同时成立。

(二)fork()函数的返回值及原因分析

问题1:

对于为什么fork给父进程返回的是子进程的PID,而子进程返回0。这是因为在所有进程就类似于一个进程树。

对于父进程来说,我需要知道我的子进程PID是什么,以进行区分因为一个父进程可以有多个子进程。 对于子进程来说,我没有子进程,只需要返回0即可。那可能会有疑惑,那我子进程是怎么找到父进程的呢?其实在之前的代码就可以知道,是有一个系统调用接口 getppid() 就可以知道自己的父进程是谁。 

所以对于父子进程来说,是一个 1:n 之间的关系。

 问题2:

为什么一个 fork() 函数会返回两次呢?其实这个问题也很好理解

那么在fork函数内会申请新的pcb,malloc一块新的空间给子进程以及拷贝父进程pcb,那么此时子进程已经创建,就会和父进程一起执行代码,到最后返回pid。所以并不是一个函数返回两次,而是有两个进程在执行同一段代码,子进程返回0,父进程返回子进程的pid。

        问题3:

                为什么一个id变量,即==0,又满足>0,if else if 同时成立。在讨论这个问题之前,我们先想一下,我此时同时打开了抖音以及微信,如果微信崩了,会不会影响抖音?显然是不会的,那么就可以得出一个结论,进程是具有独立性的

父进程创建子进程后,两个进程同时指向一份代码,但这部分代码是只读的。如果父子任何一方,对数据进行修改,那么操作系统会把被修改的数据在底层拷贝一份,让目标进程修改这份拷贝。这种操作称之为写时拷贝

(三)实验验证进程的独立性和写时拷贝

         gval是一个全局变量,父子进程都可以看到,当我们fork之后,子进程会开始对gval值进行修改,而父进程只对gval值进行查看。并且通过运行代码很明显能看到,子进程修改了gval值后,父进程的gval值并没有发生改变,这更加证明了父子进程之间是相互独立,并且如果父子一方对数据进行修改并不会影响到另一方。

                                                                        那么本片文章到这里就结束了,感谢各位观看!!!

相关文章:

  • 蓝桥杯篇---串行EEPROM AT24C02
  • 2025.2.15
  • 嵌套调用实现数组元素逆序存放
  • 算法04-希尔排序
  • spring session、spring security和redis整合的简单使用
  • LeetCode 热门100题-合并区间-不熟练
  • 人工智能之姿态估计OpenPose算法源码分析(vgg19,backbone,高斯热度图,单位向量叉乘,向量累加和后求平均,感受野,多个stage纠错)
  • 【数据结构】复杂度
  • AI前端开发与职业倦怠:效率提升的解药
  • 几款dxf文件转Gcode的开源软件
  • SQL CHECK 语句详解
  • vue不是内部或外部命令?
  • Docker 实战与应用:提升开发效率的核心命令与场景解析
  • C语言-章节 1:变量与数据类型 ——「未初始化的诅咒」
  • 相机模数转换
  • Vue响应式原理实现总结(数据劫持Object.defineProperty/Proxy+发布订阅者设计模式)
  • STM32 如何使用DMA和获取ADC
  • 5分钟了解! 探索 AnythingLLM,借助开源 AI 打造私有化智能知识库,熟悉向量数据库
  • 【Unity3D优化】AssetBundle的压缩格式优化
  • Rust 组织管理
  • 女子应聘文员被说“太丑”?官方回应:有关部门启动核查处置
  • 大风+暴雨,中央气象台双预警齐发
  • 在美国,为什么夏季出生的孩子更容易得流感?
  • 广西等地旱情缓解,水利部针对甘肃启动干旱防御Ⅳ级响应
  • 上海制造佳品汇大阪站即将启幕,泡泡玛特领潮出海
  • 与总书记交流的上海人工智能实验室年轻人,在探索什么前沿领域?