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

实用指南:【Linux】深入理解Linux的进程(一)

实用指南:【Linux】深入理解Linux的进程(一)

【Linux】深入理解Linux的进程(一)

学校OS书本讲的Linux的进程太哲学。本文将解释子进程,根据指令、代码、Linux内核源码剖析Linux的进程状态,包你听懂。

另外一提,本文命令都在云服务器上运行,先前我在WSL环境下进行测试,发现有部分指令和在传统Linux运行结果不同,比如孤儿进程的处理。(还是很坑人的这方面,所以我也推荐大家现在云服务器,或者本机虚拟机、物理机上进行学习)

冯诺依曼结构

在开始前,先讲解以下冯诺依曼的计算机结构,冯诺依曼结构十分伟大,走出了计算机进入家家户户的重要一步,另外冯诺依曼结构有助于理解CPU和内存、外设之间的关系,所以这里也会讲解一遍。

一个计算机是怎么接收数据然后进行处理、发送出去的呢?

外部信息先通过输入设备把数据传入到内存中,然后由CPU对数据进行解密、处理,再把数据加密后交给内存,内存把数据交给输出设备,输出设备把数据传到网络或者显示器等地方。

冯诺依曼结构十分厉害的一点就是把内存作为核心,都和内存打交道,而不是直接把信息交给CPU,内存成为CPU的一个缓冲区。

请添加图片描述

进程描述

学校讲的PCB完成进程描述功能,供CPU找到进程地址,进行进程调度,(我这里会重新讲,你不会也无所谓),本文将其具象化。

​ 在进入主题前,我们可以思考一下——如果对一个图书馆的书籍进行管理,应该怎么管理保障不会乱。

​ 我们可以赋予书籍不同的属性,比如:一本书可以按题材分类,也可以按主旨分类。然后把相同属性的书籍放在一起。放在计算机里,我们就需要用到struct对每本书的属性进行记录,然后在管理的时候,我们可以用到数据结构与算法的知识了。你可以顺序表,可以链表,可以哈希表,可以选择最适合完成业务需求的数据结构完成管理。

​ 上述步骤就是先描述(指创建结构体对属性进行记录的过程),再组织(将结构体封装到数据机构中,完成管理的过程)。

​ 事实上,你看到这里就已经掌握了计算机管理大部分东西的方法了。我们开始举例说明——你的计算机能够检测到你外设的状态,这就用到了先描述再组织,操作系统把不同的外设封装成不同的struct,然后进行管理。下文提到的进程也是如此。

PCB——task_struct

​ 我们看一下Linux的进程描述字段部分源码:

struct task_struct {
volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
atomic_t usage;
unsigned int flags;	/* per process flags, defined below */
unsigned int ptrace;
int lock_depth;		/* BKL lock depth */
struct list_head tasks;
struct plist_node pushable_tasks;
struct mm_struct *mm, *active_mm;
pid_t pid;
pid_t tgid;
struct list_head ptraced;
struct list_head ptrace_entry;
};

通过源码,我们可以知道所谓的PCB(进程属性描述字段)在Linux下就是task_struct,这个结构体描述了进程的所在文件位置、虚拟地址空间分配、文件打开的描述等等关键信息。注意struct list_head ptraced;,这个list_head本质就是封装了两个指针的结构体,而task_struct有很多list_head。他有什么用呢?

请添加图片描述

Linux的task_struct有很多个指针,这样可以把一个task_struct放在不同的数据结构、不同的链表、队列里了。而通过偏移量就可以准确找到想要找的元素位置——>&((task_struct*)0->link1)找到link1的偏移量。(struct task_struct*)((char*)next - x)找到下一个task_struct起始位置地址。这样,在不同数据结构下,都可以完美找到下一个/上一个进程描述字段在哪了。

加载到内存

我们写程序的过程,是向磁盘写内容的过程,那么怎么运行我们写的程序呢?结合冯诺依曼结构,CPU不能直接和磁盘打交道,所以你想要运行,需要先把你的进程放到内容中,这一过程我们叫做加载。

一个完整的进程 = task_struct + 自己的代码和数据。这个代码和数据就是在内存中。

Linux下一切皆文件,我们可以通过以下步骤深入理解代码加载到内存的过程

首先写一个死循环的程序,方便我们查看进程的状态,然后完成编译、链接、运行。(我这里编译链接生成myprocess文件)Linux操作系统给进程命名了一个id(我们把这个id叫做进程id,或者pid(process id)),并且生成这个id相同的文件夹放在/proc目录下,所以我们要先查看以下这个id是什么:我们新开一个会话,运行ps axj | head -1 ; ps axj | grep myprocess查看我们运行的程序信息,找到pid,这就是我们说的id。然后在/proc找相应的文件夹。详细操作如下:

请添加图片描述

可以在你的程序中通过chdir()系统调用修改cwd,这里就不演示了。

需要注意的内容

  • 那么怎么删除你刚刚运行的死循环的程序呢?你可以在刚刚运行的会话里输入ctrl c发送信号给进程,让他终止,kill -9 + pid也是一样的效果。不同的是,ctrl c只能给当前会话的在前台运行的进程发送终止信号,而kill -9可以给其他会话、后台的进程发送终止信号。信号部分会在后面详细讲。
  • /proc是内存级的,文件的属性和内容都放在内存,但是对于路径查找,还是走的文件系统那一套(文件系统会出详细解析的文章)。

子进程

子进程指在当前进程下创建的新进程,相应的,对于子进程,这个进程就是父进程。

fork

  • fork()命令可以创建进程,返回pid_t(long的宏定义),给父进程返回子进程的pid,给子进程返回0,如果创建进程失败会返回-1。
  • 子进程和父进程共享代码和数据,当子进程对数据进行修改,会发生写时拷贝。以方便节省内存空间,另一方面防止子父进程相互影响。(写时拷贝:指再内存上新开辟空间,同时完成页表映射的更新)
  • 那么问题来了
    • 为什么给父进程一个子进程pid,给子进程一个0,两个返回值不一样
      • 通过getppid()函数,子进程可以访问到父进程的pid,但是父进程不能通过函数查询到子进程的pid,如此返回值方便进行管理。
    • fork()创建子进程过程中完成了什么一系列操作?
      • 向操作系统申请新的task_struct空间
      • 父进程把自己task_struct原封不动copy给子进程
      • 把子进程task_struct放入runqueue
    • fork()为什么可以返回两次??
      • 完成创建子进程过程后,开始给return id;此时return id;会在父进程执行一遍,在子进程执行一遍,子进程还会发生写时拷贝,自然就可以返回两个值了。
    • 子父进程有什么关系?
      • 子进程共享父进程的代码和数据,当子进程对代码和数据有修改,会发生写时拷贝。
      • 子进程在结束时,会把return结果返回给父进程,tash_struct结构体也交给父进程回收。

进程状态

在此之前我们向讲三个系统调用,pid_t getpid()和pid_t getppid(),这两个系统调用分别是查看当前进程、父进程(父进程后文会讲)的进程id。先要查看运行的进程的进程状态还需要ps axj | head -1 ; ps axj | grep myprocess命令,你可以用while封装一下:while :; do ps axj | head -1; ps axj | grep myprocess; sleep 1; done就可以每过一秒查看一次myprocess进程的状态

使用ps axj指令查看Linux的进程状态有以下几种:(守护进程在网络通信部分讲,守护进程也是一种进程状态)

R(Running)

S(sleep可中断睡眠)

  • 可中断睡眠:阻塞导致的休眠,因为一个设备只能由一个进程使用,在此设备交给其他进程使用的时候,这个进程就会进入阻塞休眠的状态。除了这种情况,wait等待其他进程的信号也是S状态。
  • 内核上:我们上文提到过,Linux操作系统把不同设备封装成struct对象,那么,把需要这个设备的进程放到这个设备的struct结构体中,这就是阻塞。当设备空闲,要分配给其他进程时,从这个struct中寻找下一个分配的进程即可。

D(不可中断睡眠)

T

  • 暂停(因为ctrl z暂停)。
  • 可以通过jobs指令查看当前处于休眠的任务,同时会回显任务号。然后通过kill -9 %任务号指令就可以删除这个休眠的任务。也可以通过kill -SIGCONT %任务号让休眠进程继续进行。或者使用fg %任务号放在前台运行,使用bg %任务号放在后台运行。

t

Z(Zombel)

  • 僵尸状态:当子进程完成(或者被kill),子进程的PCB(tash_struct)此时没有父进程回收,就把此时的子进程状态称为僵尸状态。
  • 对于僵尸状态,如果你kill父进程,那么子进程终止的时候就会导致没有父进程回收task_struct,为了解决这一问题,操作系统会自动把你这个子进程的pid设为1,这个1其实就是bash的进程号。当你子进程运行完毕,task_struct就会交给bash回收,防止内存泄漏。

僵尸状态验证代码

#include <stdio.h>#include <unistd.h>#include <sys/types.h>int gval = 100;int main(){printf("父进程开始运行,pid: %d\n", getpid());pid_t id = fork();if (id < 0){perror("fork");return 1;}else if (id == 0){printf("我是一个子进程 !, 我的pid: %d, 我的父进程id: %d, gval: %d\n", getpid(), getppid(), gval);sleep(5);// childwhile (1){sleep(1);printf("子进程修改变量: %d->%d", gval, gval + 10);gval += 10; // 修改printf("我是一个子进程 !, 我的pid: %d, 我的父进程id: %d\n", getpid(), getppid());}}else{// fatherwhile (1){sleep(1);printf("我是一个父进程 !, 我的pid: %d, 我的父进程id: %d, gval: %d\n", getpid(), getppid(), gval);}}printf("进程开始运行,pid: %d\n", getpid());//  chdir("/home/whb");//  fopen("hello.txt", "a");//  while(1)//  {//      sleep(1);//      printf("我是一个进程 !, 我的pid: %d, 我的父进程id: %d\n", getpid(), getppid());//  }}

僵尸状态效果

请添加图片描述

X

  • 指结束的进程状态,通常情况下看不到,因为一进入X,PCB被回收,就瞬间查看不了此进程的状态。

想要验证R状态,只需要运行死循环while(1){}即可,想要验证S,只需要scanf()阻塞住即可。

一定要注意:验证R状态,万万不要涉及文件IO,涉及printf scanf之类的都不可以,IO会导致进程大多数时间处于S状态,你可能会看到S状态。

挂起状态

http://www.hskmm.com/?act=detail&tid=29287

相关文章:

  • 2025年10月铝型材源头厂家最新推荐排行榜:五大优选企业深度解析!
  • 2025外贸独立站推广最新权威推荐榜:高效引流与转化实战全解
  • 软件工程第三次作业——结对作业
  • 20232310 2025-2026-1 《网络与系统攻防技术》 实验一实验报告
  • 2025年CNC高压清洗机订做厂家权威推荐榜:技术实力与定制
  • K8s学习笔记(八) K8s资源对象 - 教程
  • 小分子抗体药物:突破传统抗体瓶颈,在精准治疗中开辟新赛道
  • python nms
  • 2025年PE涂布机定做厂家权威推荐榜:技术实力与定制服务深
  • OI 笑传 #18
  • 2025加药装置厂家权威推荐榜:精准计量与稳定运行优选指南
  • Linux文本搜索工具grep命令使用
  • 一款基于 .NET 开源免费、高效且用户友好文件搜索工具!
  • 2025上海保洁公司最新权威推荐榜:专业服务与用户口碑深度解
  • DedeCMS命令执行复现研究 | CVE-2025-6335 - 指南
  • 算法训练.16 - 实践
  • __pycache__是什么
  • 心得体会
  • 2025视频拍摄厂家最新权威推荐榜:专业设备与创意方案首选
  • Java连接MySQL数据库
  • Redis基础命令与数据结构概览
  • 2025年媒体投放机构权威推荐榜:精准策略与创新执行优选厂家
  • PHP计算过去一定时间段内日期范围函数
  • Git版本控制工具合并分支merge命令操作流程
  • 第七章 手写数字识别(终)
  • 2025南通摄影公司最新权威推荐榜:专业团队与创意服务口碑之
  • 在Kubernetes环境中引用变量的方法
  • 2025恒温恒湿车间厂家权威推荐:精密环境控制解决方案TOP
  • 2025预应力千斤顶厂家权威推荐榜:定制技术与耐用品质深度解
  • 实用指南:用Spark+Django打造食物营养数据可视化分析系统