Linux进程概念
Linux进程
一.冯诺依曼体系结构
我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。
截至目前,我们所认识的计算机,都是由一个个硬件组件而成的。
- 输入设备:键盘、鼠标、摄像头、麦克风、网卡、硬盘
- 中央处理去(cpu):含有运算器与控制器等
- 输出设备:显示器、声卡、喇叭、网卡、硬盘
运算器 + 控制器 + 其他 = CPU 快 负责计算 cpu很笨,只能被动接受别人的指令,别人的数据 cpu必须先识别指令,它具有自己的指令集
存储器 内存 较快 临时存储 cpu在读取和写入的时候,在数据层面和内存打交道。为什么?提高整机效率,cpu和外设的速度差距太大,加入内存缓冲
外设(输入设备,输出设备) 较慢 永久存储 我们将数据从外设到内存,从内存到外设,是IO的过程 INPUT/OUTPUT
由此我们可以得出结论,在数据层面
1. CPU不和外设直接打交道,和内存直接打交道
2.所有的外设,有数据需要载入,只能载入到内存中。内存写出,也一定是写到外设中
CPU不和外设直接沟通,只和内存直接打交道!
例如程序要运行必须加载到内存?为什么要加载?
CPU要执行我的代码访问我的数据,只能从内存中读取(体系结构体规定!)
二.操作系统(Operator System)
操作系统是一个进行软硬件资源管理的软件
概念
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库,shell程序等等)
设计OS的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的(稳定的,高效的,安全的)执行环境
如何理解 "管理"
管理的本质:是对数据做管理!!!
总结
计算机管理硬件
- 描述起来,用struct结构体
- 组织起来,用链表或其他高效的数据结构
系统调用和库函数概念
- 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
- 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。例如c语言中的部分函数本质上也是进行系统调用
三.进程
基本概念
课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。
描述进程-PCB
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),Linux操作系统下的PCB是task_struct
task_struct-PCB的一种
- 在Linux中描述进程的结构体叫做task_struct。
- task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
task_ struct内容分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
组织进程
可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。
查看进程
1.进程的信息可以通过/proc
系统文件夹查看
蓝色的数字就是pid。在系统中,进程也是文件,可以进入进程文件查看详细信息
在进程运行时删除程序,进程不会退出。此时,exe->那一行会变红闪烁
2.使用top
和ps
这些用户级工具来获取
下述图片为top
可以使用ps axj | head -1 && ps axj | grep 'filename'
来查询进程状态
上述图片head -1是显示表头。查自己程序的产生的进程时,第二个是查所需程序进程产生的进程
通过系统调用创建进程-fork初识
fork有两个返回值
父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
查看代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t id = fork();
if(id == 0)
while(1)
{
printf("我是子进程!pid:%d,ppid:%d\n",getpid(), getppid());
sleep(1);
}
else if(id > 0)
{
while(1)
{
printf("我是父进程!pid:%d,ppid:%d\n",getpid(), getppid());
sleep(5);
}
}
return 0;
}
四.进程状态
进程的状态
进程的状态大概为
运行 新建 就绪 挂起 阻塞 等待 停止 挂机 死亡 ........
进程如此多的状态都是未来满足不同场景运行的!
- 一个CPU一个运行队列
- 让进程进入队列,本质:将该进程的task_struct结构体对象放在运行队列中!
- 进程PCB在runqueue,就是R,不是这个进程正在运行才是运行状态
- 不要只意味,你的进程只会等待(占用)CPU资源,你的进程,也可能随时随地要外设资源!!!
- 所谓的进程不同的状态,本质是进程在不同的队列中,等待某种资源!
- 阻塞 VS 挂起: 阻塞不一定挂起,挂起一定阻塞
- 除了运行状态(概率特别小),其他状态均有可能挂起
Linux内核源代码
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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 */
};
- R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))可以被终止。
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束,在该状态无法被操作系统杀掉,只能通过断电和进程醒来杀死。
- T停止状态(stopped): 可以通过发送 19) SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 18)SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
我们可以看到进程目前的状态为S+,但是我们的程序一直在朝外打印东西
这其实是由于外设打印到显示器上特别慢,等待显示器就绪,要花很长的时间,99%的时间都是在等待IO就绪,1%再执行打印代码
进程状态查看
ps axj | grep '进程名'
ps axj |head -1 && ps axj | grep '进程名' #为了看起来更方便,我们给他加上表头
进程状态控制
语法:kill [选项] PID
可以通过kill -l
来查找选项
我们实践暂停一下进程
将其恢复,却发现他的加号已经消失了
带加号表示前台进程,没有加号表示后台进程,后台进程无法使用Ctrl c
终止,只能使用 kill -9 PID
的形式终止
僵尸进程(Zombie Process)
概念
- 僵尸进程是指一个子进程已经结束(即已经运行完毕),但是其父进程尚未通过 wait 或 waitpid 等系统调用回收其结束状态(也就是它的资源信息,包括 PID 等进程标识符)时的状态。这个时候,子进程就变成了僵尸进程。
- 例如,假设有一个父进程 Fork 了一个子进程去执行一个简单的任务(如计算一个简单的数学公式)。子进程完成后,它需要向父进程回报自己的状态。但如果父进程没有使用 wait 或 waitpid 等调用去获取子进程的状态,那么子进程就会变成僵尸进程。即使子进程的程序代码已经完全运行结束,它还占用着系统中一些资源,如进程表项信息等。
特点
- 资源占用:
◦ 僵尸进程的 task_struct(PCB)会一直保留在内存中,包含进程的退出状态、资源使用统计等信息。
◦ 内核需要维护这些数据以供父进程查询,因此僵尸进程的 PID、退出码、运行时间 等基本信息不会被释放。
◦ 虽然僵尸进程已不执行代码,但其占用的内核资源(如 PCB 结构体)会导致内存泄漏。
孤儿进程(Orphan Process)
概念
- 孤儿进程是指父进程在子进程之前结束,而子进程仍在运行的情况。子进程因为父进程的消亡而失去了原本的 “赞助人”,成为孤儿进程。在这种情况下,孤儿进程会被操作系统收养,通常由 init 进程(在类 Unix 系统中,PID 为 1 的进程)接管,成为 init 的子进程。
- 举个例子,如果父进程启动了一个子进程来执行某个长期运行的任务(如一个后台数据处理任务),然后父进程自己因为某种原因(例如出现致命错误,或者被用户 kill 掉)结束了。此时子进程就成了孤儿进程。不过,init 会成为它的新的父进程,接管它的运行。
特点
- 它们还在执行任务。孤儿进程本身可能仍然在执行有用的系统任务。比如,一个父进程 fork 出一个子进程去做一个复杂的计算任务,父进程因为某些意外(比如被系统强制终止)结束了,而子进程还在进行计算。这时候子进程成为孤儿进程,但它的计算任务依然在继续进行。
- 如果是前台进程创建的子进程,如果孤儿了,会自动变成后台进程
五.进程优先级
基本概念
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
查看系统进程
在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:
我们很容易注意到其中的几个重要信息,有下:
- UID(User ID):它是进程所有者的用户 ID。在系统中,每个用户都有一个唯一的用户 ID,用于标识用户身份。
- PID(Process ID):这是进程的唯一标识符。每当系统创建一个新进程时,都会为它分配一个唯一的 PID。PID 的作用就像人的身份证号码一样,用于在系统中唯一地识别一个进程。
- PPID(Parent Process ID):它表示进程的父进程 ID。在操作系统中,一个进程可以创建新的进程,创建者被称为父进程,被创建者是子进程。PPID 就是用来标识子进程的父进程的 PID。
- PRI(Priority):进程优先级。它用于确定进程在 CPU 调度中的优先顺序。操作系统会根据 PRI 值来分配 CPU 时间,优先级高的进程会更优先地获得 CPU 资源。其值越小越早被执行
- NI(Nice Value):它与进程优先级有关,用于调整进程的优先级。Nice 值的范围通常是从 - 20 到 19。值越小,进程的优先级越高;值越大,优先级越低。用户可以通过修改 Nice 值来调整进程 的优先级,以平衡不同进程对系统资源的使用。
查看进程优先级的命令
ps -al #显示所有用户的所有进程(包括与其他用户的进程),并且以详细格式列出。
ps -l #显示当前终端的进程和没有控制终端的进程,并以详细格式列出。
上述两种方式都可以查进程的优先级
用top命令更改已存在进程的nice:
- top (修改NI,此操作需要提权或使用root账号才能修改)
- 进入top后按“r”–>输入进程PID–>输入nice值
Nice 值的范围通常是从 - 20 到 19。值越小,进程的优先级越高;值越大,优先级越低。用户可以通过修改 Nice 值来调整进程 的优先级,以平衡不同进程对系统资源的使用。
使用top设置优先级重复设置不会叠加,每一次设置都是重新从80进行加减nice值。
其他概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架