【Linux】初识进程(Ⅰ)
1.认识冯诺依曼系统
冯诺依曼体系的核心思想就是存储思想:计算机的程序和程序所处理的数据一样,都以二进制的形式存储在同一个存储器中。
运算器
功能: 负责执行所有的算术运算和逻辑运算。
例子: 加减乘除、与或非比较等操作。
控制器
功能: 是整个计算机的指挥中心。它从存储器中读取指令,进行分析,然后向其他部件发出控制信号,协调各部件步调一致地工作。
运算器 + 控制器 = 中央处理器,也就是我们常说的 CPU。
存储器
功能: 用于存放程序和数据。所有信息(指令和数据)都以二进制形式存储在此。
特点: 这是“存储程序”概念的关键,程序和数据共享同一个存储空间。
例子: 内存(RAM)。
输入设备
功能: 用于将程序和数据输入到计算机中。
例子: 键盘、鼠标、扫描仪等。
输出设备
功能: 用于将计算机处理的结果展示出来。
例子: 显示器、打印机、音响等
关于存取速度:
2. 操作系统
2.1 概念
操作系统是管理计算机硬件与软件资源的系统软件,是计算机系统的内核与基石。它作为硬件之上的第一层软件,为其他应用程序提供运行基础,并在用户与计算机硬件之间扮演着中介角色。
2.2 功能
操作系统的两大核心职责
对下管理硬件
任务: 负责高效、公平地管理计算机的所有硬件资源,包括:
处理器(CPU)管理: 决定哪个程序在何时使用CPU(进程调度)。
内存(Memory)管理: 为程序分配和回收内存空间,保证彼此不干扰。
设备(I/O)管理: 驱动并控制键盘、鼠标、显示器、磁盘等外部设备。
文件(File)管理: 以目录树的形式组织磁盘上的数据,负责文件的存储、检索、共享和保护。
对上提供服务
任务: 通过系统调用(System Calls) 为应用程序提供使用硬件资源的接口,避免了应用程序直接操作硬件的复杂性。同时,它为用户提供了方便操作计算机的接口(如图形界面或命令行)。
3. 进程基础概念
3.1 概念
进程(Process) 是计算机中正在运行的程序的一个实例。它是操作系统进行资源分配和调度的基本单位。
进程是动态的,我理解的就是代码运行的过程。既然如此,那么操作系统是怎么把进程管理起来的呢?
先描述,再组织
我们先来描述进程的所有信息,而要描述一个物体,我们最快想到的应该就是用结构体,从而用这个结构体来表示这个进程,所以操作系统只用管理这些结构体就可以达到管理进程的目的了。
3.2 PCB——描述进程
PCB (Process Control Block):是一个通用概念,是操作系统理论中对于“用来描述和管理进程的数据结构”的统称。这是一个抽象术语。
在Linux下,PCB就是:task_struct结构体
task_struct 是 Linux 内核的⼀种数据结构类型,它会被装载到RAM(内存)并里且包含着进程的信息。所有运行在系统里的进程都以 task_struct 双链表的形式存在内核里。
3.3 查看进程
查看进程就是查看proc这个文件:
命令:ls /proc/
4. 进程状态
进程运行时有不同的状态,为了能够弄清有哪些状态,我们可以看一下在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(runing):可运行状态。但这并不是只指进程正在 CPU 上执行,而是指进程正在CPU上运行或在就绪队列中等待运行。
S(sleeping):可中断睡眠状态。进程正在等待某个事件的完成(如等待用户输入、等待网络连接、等待一个信号),在这种状态下睡眠的进程可以被信号中断或唤醒。
D(deep sleeping):深度睡眠/不可中断睡眠。处于D状态的进程不能被任何信号中断或杀死,即使是kill也不行。这是为了确保进程在等待磁盘操作时不会被打断,从而避免数据不一致或损坏这种内核级别的严重问题。你可能会在系统繁忙等待磁盘时看到这种状态。
T (stopped):停止状态。进程的执行被暂停(挂起),例如:调试器(如 gdb)在设置断点时,会暂停被调试的进程,使其进入此状态。
t (tracing stop): 这是T状态的一个特例,专门表示进程正在被调试器跟踪而停止。例如被 gdb attach 住,在断点处停止
X (dead):进程已经彻底结束死亡,其资源(包括 PCB)都已经被父进程回收。这是一个瞬时状态,你几乎不可能在ps命令中看到它,因为进程一旦终止并回收,就从系统列表中消失了。
Z (zombie):进程已经终止,但其父进程还没有调用wait()来回收它。内核会保留它的进程描述符(PCB)中的一些退出状态信息,直到父进程读取为止。处于这种状态的进程就是“僵尸进程”(Zombie)。如果父进程先退出,这些僵尸进程会被init进程(PID 1)接管并回收。如果大量僵尸进程不回收,会浪费内核的进程表资源。
4.1 查看进程状态
命令: ps aux/axj 命令
• a:显示⼀个终端所有的进程,包括其他用户的进程。
• x:显示没有控制终端的进程,例如后台运行的守护进程。
• j:显示进程归属的进程组ID、会话ID、父进程ID,以及与作业控制相关的信息
• u:以用户为中心的格式显示进程信息,提供进程的详细信息,如用户、CPU和内存使用情况等
下面我们来模拟几个进程的状态,用我们刚学到的命令来查看一下:
4.1.1 查看进程R状态
我现在写了一个死循环的process.c:
将其可执行文件命名为process:
我们可以看到它的R状态,之后我们会解释为什么会有个加号
那么为什么会有第二行的内容呢?因为Linux下一切皆文件,所以我们上面输入grep命令其实也是文件,它运行时也是一个进程,所以我们查找的时候都会查到命令本身,之后我们就直接忽略掉这行就行了
4.1.2 查看进程S状态
写一个需要用户输入数据的程序:
程序会一直等待用户输入,等待输入过程就是S状态
所以此时我们可以看到进程是S状态
4.1.3 查看进程D状态
让系统进入D状态(不可中断睡眠)是一种极其危险的操作,因为它会锁死一个或多个关键进程,甚至可能最终导致整个系统无响应。
所以这里我就不给大家演示了,如果有好奇的小伙伴可以自己问一下AI怎么让进程出现D状态,会有教程的哈,只不过恢复可能就比较麻烦了,实在不行可以重装系统嘛,哈哈哈哈哈
4.1.4 查看进程 t 状态
我在调试程序时打了一个断点,再运行程序,此时程序会在断点处停下
然后我们再查看进程状态,此时是 t 状态
其他状态都不好模拟出来,我们就不演示了
4.2 僵尸进程
4.2.1 僵尸进程的概念
僵尸进程是指已经执行完毕、但其退出状态尚未被父进程收集(读取)的进程。
在进程退出时,操作系统并不会立即清除其所有痕迹,而是会保留一些基本信息(如进程ID、退出状态、运行时间等),直到父进程通过 wait() 或 waitpid() 系统调用来读取这些信息。
下面让我们来创建一个30s的僵尸进程:
运行程序:
查看进程状态:
4.2.2 僵尸进程的危害
浪费系统资源:每个僵尸进程都占用一个进程ID
进程ID耗尽:如果产生大量僵尸进程,可能导致无法创建新进程(系统有最大进程数限制)
系统性能问题:大量的僵尸进程会影响系统性能
如果系统中已经存在僵尸进程,我们该怎么处理呢?
重启父进程:杀死父进程,僵尸进程会被init进程收养并回收,不要尝试杀死僵尸进程,僵尸进程是无法被杀死的。
系统重启:如果僵尸进程太多,最彻底的方法是重启系统
4.3 孤儿进程
孤儿进程是指父进程已经终止或退出,但子进程仍在运行的进程。当一个进程的父进程先于子进程结束时,这些子进程就成为了"孤儿"。
在Unix/Linux系统中,为了解决这个问题,init进程(进程ID为1)会自动收养所有孤儿进程,成为它们的新父进程。
下面我们来创建一个孤儿进程:
运行这个程序:
查看进程状态
前面子进程和父进程都在运行,之后就只有子进程在运行了,此时子进程就是孤儿进程了
5.进程优先级
5.1 基本概念
进程优先级决定了进程获得CPU执行时间的优先顺序。因为CPU资源是有限的,当多个进程都处于可运行状态时,操作系统需要根据优先级来决定哪个进程先使用CPU。
Linux系统下,进程优先级由PRI和NI决定。
5.2 PRI 和 NI
1. PRI (Priority) - 最终优先级
含义:进程实际的优先级,这个值由内核动态调整
范围:通常为 0-139(数值越小,优先级越高)
特点:用户无法直接修改PRI值
2. NI (Nice) - 友好值
含义:用于影响PRI值的修正量
范围:-20 到 19(数值越小,优先级越高)
特点:用户可以通过修改NI值来间接调整进程优先级
进程优先级计算公式:PRI(new) = PRI(old) + NI
5.3 查看系统进程
命令:
ps -l #查看当前shell进程信息
ps -la #查看所有进程的详情
top
• UID:代表执行者的身份
• PID:代表这个进程的代号
• PPID:代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
5.4 修改进程优先级
进入top命令:
输入r--> 输入要修改的进程ID号 --> Enter
输入要修改NI值,假如输入10
我们就可以看到PR和NI值都改变了
但是NI值是有范围的,上面也已经说过了
6. 命令行参数和环境变量
6.1 基本概念
命令行参数是程序运行时从命令行传递的参数。
环境变量是系统或用户设置的全局变量,所有进程都可以访问。
6.2 查看命令行参数
下面我们通过一段程序来查看:
参数说明:
argc:参数计数(包括程序名本身)
argv[]:参数值数组(字符串指针数组)
argv[0]:程序名
argv[1] 到 argv[argc-1]:用户传递的参数
编译运行该程序:
6.3 查看环境变量
查看环境变量的方法有很多种,其他方法我们之后再讲,本篇文章我们先通过一段main函数的第三个参数的方式来查看:
编译运行可执行程序:
常用环境变量: