news 2026/7/10 15:09:25

第六部分:Linux进程初识

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第六部分:Linux进程初识

目录

1、冯诺依曼体系

2、操作系统

3、进程

3.1、描述进程的结构

3.2、组织进程

3.3、查看进程

3.4、fork调用初识

4、进程状态

4.1、一般进程的状态

4.2、Linux进程状态

4.2.1、R状态

4.2.2、S状态

4.2.3、D状态

4.2.4、T状态

4.2.5、t状态

4.2.6、X状态

4.2.7、Z状态

5、孤儿进程

6、进程优先级

6.1、查看

6.2、修改

6.3、原理

6.4、补充


1、冯诺依曼体系

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系

截至目前,我们所认识的计算机,都是有一个个的硬件组件组成的:

运算器:对我们的数据进行计算(算数运算和逻辑运算)。

控制器:对计算机的硬件流程进行一定的控制。

中央处理器(CPU):由控制器加运算器组成。

输入设备:例如键盘、鼠标、扫描仪、写板等。

输出设备:例如显示器、打印机等。

注意:磁盘本质上即是输入设备又是输出设备。网卡和磁盘类似,是输入设备也是输出设备。

外设:一般就是指输入和输出设备。

注意:图中的这五个部分都是独立的,各个硬件单元必须用线连接起来成为一个整体,这个线可以分为两类,分别是系统总线IO总线,合称总线。CPU和内存之间的的线是系统总线;内存和外设之间的线是IO总线。

关于冯诺依曼,必须强调几点:这里的存储器指的是内存,不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设 ,外设要输入或者输出数据,也只能写入内存或者从内存中读取。 一句话,所有设备都只能直接和内存打交道。

上面的三级缓存和寄存器全部集成在现代CPU的内部。

2、操作系统

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

1、内核(进程管理,内存管理,文件管理,驱动管理)。

2、其他程序(例如函数库,shell程序等等)。

操作系统与硬件交互,管理所有的软硬件资源目的是为用户程序提供一个良好的运行环境。在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件。

操作系统为了保证自己的数据安全,同时也保证给用户提供服务,操作系统以接口的方式给用户提供调用的入口,来获取操作系统内部的数据。这个接口本质上就是一个函数,所有访问操作系统的行为,都只能通过系统调用完成。我们把基于系统接口的开发称为系统编程,未来要讲到的主要是系统调用接口和用户操作接口、用户这三个部分上的开发。

系统调用和库函数:在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成,有了库,就很有利于更上层用户或者开发者进行二次开发。

操作系统是通过先描述、再组织的方式管理计算机的。描述使用struct结构体进行描述,组织则使用各种数据结构进行组织。

3、进程

直观概念:程序的一个执行实例,正在执行的程序等。

内核概念:担当分配系统资源(CPU时间,内存)的实体。也有的把进程称之为任务

通俗的讲,进程就是一个加载到内存中的程序叫做进程。

一个操作系统,不仅仅只有一个进程,而是有很多进程,所以操作系统必须把进程管理起来。

3.1、描述进程的结构

人辨别和认识一个事物或者对象都是通过属性辨别和认识的。进程属性就被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。

课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct

task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

任何一个程序在加载到内存形成真正的进程时,操作系统都要创建描述进程的结构体对象。

进程常见的属性有:

1、标示符: 描述本进程的唯一标示符,用来区别其他进程。

2、状态: 任务状态,退出代码,退出信号等。

3、优先级: 相对于其他进程的优先级。

4、程序计数器: 程序中即将被执行的下一条指令的地址。

5、内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

6、上下文数据: 进程执行时处理器的寄存器中的数据。

7、I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

8、记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

9、其他信息。

这些都是比较常见的属性,实际上task_struct结构体是非常大的,里面的属性是非常多的。

3.2、组织进程

Linux内核中,最基本的组织进程的方式就是采用双向链表组织的。

因为有很多进程,在操作系统中,对进程进行管理,就变成了对链表进行增删查改,链表的每一个节点就是一个进程。

此外,想要做哪一方面的管理,就把对应的进程数据结构对象放到某一个组织的数据结构中。

注意:所谓的对进程进行管理本质就是操作系统对内核PCB数据结构对象进行管理,而不是直接管理的代码和数据。

总的来讲:进程=内核PCB数据结构对象+代码和数据。

3.3、查看进程

进程的信息可以通过 /proc 系统目录查看。例如:

ls /proc

/proc中存放着系统中所有动态的进程信息。例如:

其中每一个蓝色的目录名都是进程的PID,目录中存放了该进程的信息。我们可以查看目录中的内容,例如:下面的图片仅仅展示了部分信息。

其中exe指向的就是该可执行程序,cwd指向的是可执行程序的工作目录(也就是该可执行程序执行时所处的目录)。

ps命令:

语法:ps [选项]

功能:用来查看当前系统进程。

常用选项:

-e:显示所有进程。

-x:显示没有控制终端的进程。

-u [username]:显示某个用户的进程。

-a:显示所有的进程,包括系统进程和用户进程。

-f:使用完整格式显示信息,包括UID、PID、PPID等。

-j:显示进程组和会话信息。

-l:显示长格式,包括更多的状态信息。

例如:

ps ajx

如我们要查看某个进程的信息,例如:

ps ajx | head -1 && ps ajx | grep test01

查看test01进程的信息。中间的&&也可以使用 ; 代替。还可以这样写,用来排除掉grep命令的进程信息,例如:

ps ajx | head -1 ; ps ajx | grep test01 | grep -v grep

我们可以编写一个小脚本来每隔一秒监视一次test01进程信息,例如:

while :; do ps ajx | head -1 ; ps ajx | grep test01 | grep -v grep ; echo "------------------"; sleep 1 ;done

我们可以使用kill -9 PID的方式干掉进程,关于kill后面再详细讲,其中PID是进程的唯一标识符,相当于进程的名字,PPID是该进程的父进程的的PID。我们每次使用的指令(常规指令)的父进程就是bash进程。我们可以使用getpid()和getppid()接口获取进程的PID和PPID,例如:

#include<stdio.h> #include <sys/types.h> #include <unistd.h> int main() { printf("pid:%d ppid:%d\n",getpid(),getppid()); return 0; }

可以使用man命令来查看这两个接口的信息,其中pid_t这个类型本质上是无符号整型。

3.4、fork调用初识

功能:fork是用来创建子进程的

#include <unistd.h> pid_t fork(void);

成功的话给子进程返回0,给父进程返回子进程的PID。失败的话给父进程返回-1。

一般而言,fork之后的代码父子共享,当父或子进程不修改数据时,数据也是共享的。但如果父或子进程对数据进行修改的话,数据就不共享了,而是各自有一份,这是通过写时拷贝的方式实现的;内核会给先写入的进程开辟新的空间用来写入,然后另一方拥有原本共享的那份数据。所以子进程和父进程数据的独立性导致进程在运行时是具有独立性的。

该接口有两个返回值是为了区分不同的进程。本质上fork函数内部返回部分的语句同属于父子进程,所以才会看到有两个返回值的现象,也就是返回值语句分别被父子进程各执行一次。

一般fork之后的代码使用if进行分流。例如:

#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t ret = fork(); if (ret > 0) { printf("i am parent:%d\n", getpid()); sleep(10); } else if (ret == 0) { printf("i am child:%d\n", getpid()); sleep(5); } else { printf("error\n"); } return 0; }

注:创建子进程成功后,谁先运行由调度器决定,所以子进程和父进程谁先运行是不确定的。

4、进程状态

每个进程都有一个叫做时间片的概念,这个时间片就是该进程在CPU上运行的时间,不管程序是否能够执行完,耗尽时间片的时间,进程就要从CPU上下来,如果该进程执行完了,那就结束了,如果该进程没有执行完,那就会排队等待再次执行。因此在一个时间段内所有的进程都会被执行。进程的调度就是通过进程切换和时间片实现的。

一个CPU上的一个核只能执行一个程序,因为进程的数量远多于CPU的核,这样就会有大量的进程排队依次放到CPU上,然后再拿下来,我们把这种现象称为进程切换。因为CPU太快,直观是感受不到这种进程切换的,正因为CPU很快,所以才会感觉多个进程似乎是同时执行的。一个CPU的一个核只有一个运行队列,因此一个CPU的核只能执行一个程序,CPU有几个核就有几个运行队列。

4.1、一般进程的状态

就绪:进程已经加载到内存,并且已具备执行条件,但由于CPU资源的不足而无法立即执行。就绪队列中保存着所有在等待CPU的进程。

运行:运行状态的进程是指正在运行的进程或者在运行队列上的进程。

以队列的方式对进程进行调度,想要运行一个进程,首先要把进程的数据结构对象链入到运行队列中进行排队。

调度器其实就是函数,可以将运行队列作为参数传递进来,所以调度器就可以找到所有排队的进程,进行调度。所有处于运行队列的进程以及正在运行的进程处于的状态就是运行态。运行态的含义就是进程已经准备好了随时都可以被调度。

阻塞:进程因为等待某个事件(例如 I/O 操作完成)而不能继续执行。也就是说,进程无法继续执行,直到它所等待的事件发生。

在操作系统中底层会存在很多硬件,在体系结构中,所有的外设,对于系统来讲,无非就是从外设中读取到内存或者从内存写入到外设。操作系统对外设的管理也是先描述、再组织。每一个设备都会有等待队列,在特定设备的等待队列上等待的进程就处于阻塞状态。

例如以键盘为例,当进程等待键盘输入时,驱动程序读取键盘,发现读到了数据后(若发现没有数据,就会让在等待队列上的进程一直等待),此时就会将等待键盘设备的进程描述对象从键盘设备上拿下来,然后进程进入就绪状态,最后放入到运行队列中,当该进程被调度时,自然而然就可以从键盘中获取数据。

注:进程在等待外部设备时,也就是处在等待队列时,如果系统资源不足时,操作系统会将一些处于等待队列的进程的代码和数据放入到外存中,在内存中仅保留进程的PCB,这种进程状态就处在下面要说的挂起状态。

挂起:挂起的进程是指由于某种原因被操作系统主动暂时停止执行的进程,可以理解为一种更深层次的阻塞状态。挂起是指进程因操作系统的管理需求,被主动暂停并从内存中移除或临时保留。进程的状态和所有信息会保存在磁盘等存储设备中。例如:当内存资源不足时,为了腾出一些空间,会把一些进程的代码和数据放到磁盘中,等需要的时候再拿出来。

终止:进程的执行已经完成,已被系统释放相关资源。

4.2、Linux进程状态

static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };
4.2.1、R状态

R运行状态:并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里(也就是在等待执行)。例如:

注意:R+的意思是该程序在前台执行,其中+号就是指前台。R的意思是在后台运行,若想要程序在后台执行,可以使用&的方式让可执行程序在后台执行,例如:

./myproc &

另外要说明的是,后台执行的可执行程序是没办法使用ctrl+c终止的,要使用kill来干掉进程才行。

4.2.2、S状态

S睡眠状态:是指一个进程由于等待某个事件的发生而暂时停止的状态,这里的睡眠有时候也叫做可中断睡眠状态。可以响应请求,可以被kill命令干掉。例如:

#include <stdio.h> int main() { int n = 0; scanf("%d", &n); printf("%d\n", n); return 0; }

当执行该程序时,运行到scanf函数时,该进程就会进入S状态,可用命令查看该进程的状态,例如:

4.2.3、D状态

D磁盘休眠状态:有时候也叫不可中断睡眠状态,在这个状态的进程通常会等待磁盘IO完成。这个状态通常是为了确保重要的操作有条不紊地完成。不响应请求,不可以被kill命令干掉。

4.2.4、T状态

T停止状态:可以通过发送信号给进程来停止进程。这个被暂停的进程可以通过发送信号让进程继续运行。(关于信号后面再说)。可以使用

kill -19 or -18 PID的方式暂停或启用进程

例如:

4.2.5、t状态

t追踪停止状态:指的是对进程的跟踪或调试操作的停止。例如在gdb调试可执行程序时,在断点处停下来的时候,进程就处于该状态。例如:

4.2.6、X状态

X死亡状态:这个状态只是一个返回状态,我们不会看到这个状态。也就是释放进程占有的各种资源,就相当于进程运行结束。该状态很难被看到,因为太快了,不再演示。

4.2.7、Z状态

Z僵尸状态:是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。一个进程结束时,首先进入Z状态,再进入X状态。例如:

#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t id = fork(); if (id > 0) { printf("parent:%d\n", getpid()); sleep(100); } else if (id == 0) { printf("child:%d\n", getpid()); sleep(10); } else { printf("error"); } return 0; }

等待几秒后查看该进程就可以观察到僵尸进程,如下

僵尸进程的危害:

进程的退出状态必须要通知父进程,因为他要告诉关心它的父进程,你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态,退出状态本身就是需要用数据进行维护的,也属于进程基本信息,换句话说,Z状态一直不退出,PCB一直都要维护,那如果一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存,最终就可能会造成内存泄漏,关于如何解决这个问题后面再说。

注:当系统压力很大时,系统可能会主动干掉一些系统认为不重要的进程。

5、孤儿进程

父进程先退出,子进程就被称之为孤儿进程,孤儿进程的父进程会被改为1号进程(该进程就是操作系统),也就相当于被一号进程领养了。之所以会这样,是因为孤儿进程未来也会退出,资源也是要被释放的。例如:可以使用下面的代码验证

#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t id = fork(); if (id > 0) { printf("parent:%d\n", getpid()); sleep(10); } else if (id == 0) { printf("child:%d\n", getpid()); sleep(100); } else { printf("error"); } return 0; }

运行结果为:

注:进程之间的父子关系就构成了多叉树的结构,在系统中,组织进程的数据结构并不是单一的,而是同时存在多种数据结构,即一个进程同时被多个数据结构组织。 例如:一个进程的数据结构对象既是双向链表中的结点,也是多叉树的结点。

6、进程优先级

cpu资源分配的先后顺序,就是指进程的优先级。 优先级高的进程有优先执行权利。

6.1、查看

在linux中,用ps –l命令则会类似输出以下几个内容:

我们很容易注意到其中的几个重要信息,有下:

UID : 代表执行者的身份 ,UID就是用户的编号,可以使用ls -n来查看当前用户的编号即UID

PID : 这个进程的编号。

PPID :代表这个进程是由哪个进程发展而来的,即父进程的编号。

PRI :代表这个进程可被执行的优先级,其值越小越早被执行。

NI :代表这个进程的nice值,该值是进程优先级的修正数据。

进程的默认优先级为80,nice值的取值范围为-20到-19,总共40个级别,经过nice值的调整,进程优先级的数值变化范围为60到99。

总的来说:PRI(new)=PRI(old)+nice,这里需要注意的是PRI(old)永远为80,而不是上一次的PRI(new)的值。

需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。可以理解nice值是进程优先级的修正数据。

6.2、修改

一般情况下,不要改动进程的优先级。可以使用top来改变进程的优先级,先输入top,然后输入r,然后输入进程的PID,最后输入nice值即可,如果要退出top,点击q即可。

此外,还可以使用nice和renice调整优先级,如果感兴趣可以自行查阅。

例如:

把该进程的优先级改为了99。

6.3、原理

在运行队列中,有一个isempty,对应的比特位表示running中元素是否为空。run指向running,running这个数组中放的内容就是指向要运行的进程。wait指向waiting,当在调度running中的进程时,新来的要被调度的进程会放到waiting这个数组中,当running中的进程调度完毕后,交换run和wait的值,然后继续进行调度。

遍历running数组时,整体从上到下,从左到右。这就进程优先级的原理。

6.4、补充

竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。

独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。

并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

CPU中是有很多寄存器的,系统是可以通过程序计数器(也是一个寄存器)来得知进程当前执行到哪一行代码,函数返回值之所以可以被外界拿到,也是因为寄存器。进程高频的数据就会放入寄存器中,CPU中寄存器保存的是与进程相关的临时数据,也称为进程的上下文。进程在CPU上离开的时候,要将自己的上下文数据保存好,保存的目的是在未来可能要进行恢复。进程在被切换的时候:1、保存上下文 2、恢复上下文。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 15:07:24

GEO系统如何解决网站流量下降问题

一、痛点深度剖析&#xff1a;流量红利消退&#xff0c;传统SEO为何失灵 我们团队在实践中发现&#xff0c;近年来大量企业网站正面临一个残酷现实&#xff1a;精心打磨的内容即使被搜索引擎收录&#xff0c;点击率与转化率仍在持续走低。数据显示&#xff0c;超过60%的搜索查…

作者头像 李华
网站建设 2026/7/10 15:04:59

如何快速上手Vue2项目:基于vue2-demo的环境搭建与基础语法教程

如何快速上手Vue2项目&#xff1a;基于vue2-demo的环境搭建与基础语法教程 【免费下载链接】vue2 &#x1f4af; 比较好的vue2项目,值得借鉴与学习 项目地址: https://gitcode.com/gh_mirrors/vue/vue2 Vue2作为一款经典的前端框架&#xff0c;凭借其轻量、易学的特性成…

作者头像 李华
网站建设 2026/7/10 15:04:00

TW-Security-and-CTF-Resource:Web安全學習的7個必備資源與工具

TW-Security-and-CTF-Resource&#xff1a;Web安全學習的7個必備資源與工具 【免费下载链接】TW-Security-and-CTF-Resource 台灣資安 / CTF 學習資源整理 项目地址: https://gitcode.com/gh_mirrors/tw/TW-Security-and-CTF-Resource 台灣資安學習資源整理TW-Security-…

作者头像 李华
网站建设 2026/7/10 15:02:22

3分钟快速掌握Ai2Psd:AI到PSD无损转换的完整指南

3分钟快速掌握Ai2Psd&#xff1a;AI到PSD无损转换的完整指南 【免费下载链接】ai-to-psd A script for prepare export of vector objects from Adobe Illustrator to Photoshop 项目地址: https://gitcode.com/gh_mirrors/ai/ai-to-psd 你是否曾经为Illustrator到Photo…

作者头像 李华
网站建设 2026/7/10 14:57:50

kkFileView:企业级文件在线预览解决方案的技术实现与应用实践

kkFileView&#xff1a;企业级文件在线预览解决方案的技术实现与应用实践 【免费下载链接】kkFileView Universal File Online Preview Project based on Spring-Boot 项目地址: https://gitcode.com/GitHub_Trending/kk/kkFileView kkFileView是一款基于Spring Boot构建…

作者头像 李华
网站建设 2026/7/10 14:56:13

北京华恒智信为中端酒店搭建低成本即时激励体系破解保洁人员流失难题

一、中端酒店保洁岗位普遍面临的经营困境 中端酒店基层保洁岗位长期深陷薪资性价比低、工作强度大、人员流失率高的恶性循环&#xff0c;成为制约酒店服务质量与运营效率的核心痛点。行业普遍现状为保洁员月薪整体偏低&#xff0c;固定工作任务繁重&#xff0c;每日需完成大量…

作者头像 李华