Linux进程概念

发布于:2025-05-31 ⋅ 阅读:(24) ⋅ 点赞:(0)

获取进程pid方式

1.获取所有的pid信息

部分获取ps ajx | head -1 && ps axj | grep 文件名

ps ajx | head -1 && ps axj | grep 文件名 | grep -v grep可以过滤掉grep中携带进程部分信息的内容

因为我没有运行test.o所以未开辟进程,所以没有进程信息

ps ajx | grep 文件名

2.示例代码

每个进程都有属于自己的PID(身份码)

在Linux系统里,PID即“Process IDentifier”,是进程标识符 ,以下是详细介绍:
 
基本概念

PID是系统为每个进程分配的独一无二的非负整数 。每个进程从创建起就被赋予特定PID,借此在系统中被唯一标识。就像现实中每辆车都有独特车牌号,方便交通管理,PID便于操作系统对进程进行管理和区分。

作用
 
- 进程管理:是进程的身份标识。系统借助PID识别、跟踪、控制进程,如使用  kill  命令通过指定PID向进程发送信号,实现进程终止(如  kill -9 <PID>  可强制终止进程 ); waitpid  函数能让父进程等待特定子进程结束 。

- 资源分配与调度:操作系统依据PID管理进程资源分配,决定CPU时间、内存等资源如何分配给各进程,保障系统高效稳定运行 。

- 进程间通信:进程可利用PID识别通信对象,实现进程间通信和同步协作 。

回顾上文我们简单的了解了进程,首先介绍的是fork.

在Linux中, fork  是一个用于创建新进程的系统调用函数 ,它在Unix和类Unix操作系统中被广泛使用。以下是关于它的详细介绍:
 
函数原型与头文件
 
- 原型: pid_t fork(void);  , pid_t  实质是  int  ,在  <sys/types.h>  中定义 。
- 头文件:使用时需包含  <unistd.h>  ,有时也会用到  <sys/types.h>  。
 
工作原理
 
调用  fork  时,系统会创建一个与调用进程(父进程)几乎完全相同的新进程(子进程)。具体过程如下:
 
1. 资源分配:系统为新进程分配资源,如存储数据和代码的空间。
2. 数据复制:将父进程的很多属性复制到子进程,包括数据空间、堆、栈等资源的副本,但父子进程并不共享这些存储空间 。不过,代码段是共享的,即父子进程从  fork  函数调用之后的下一条指令开始执行。


 
返回值特点
 
 fork  调用一次却返回两次,在不同进程中有不同返回值:
 
- 在父进程中:返回新创建子进程的进程ID(PID),是一个大于0的整数 。通过这个返回值,父进程能识别并后续操作子进程。
- 在子进程中:返回值为0 。子进程可据此判断自身身份。
- 出错时:返回 -1 ,可能原因有当前进程数达到系统上限( errno  设为  EAGAIN  ) 或系统内存不足( errno  设为  ENOMEM  )。
 
 
在上述代码中,执行  fork  后产生父子进程。子进程中  fpid  为0,打印子进程相关信息;父进程中  fpid  是子进程PID,打印父进程及子进程PID信息 。
 
应用场景
 
- 多进程并发服务器:有客户端连接时,父进程用  fork  创建子进程处理请求,自身继续监听新连接,提升并发处理能力 。
- 任务并行处理:将不同任务分发给子进程并行执行,加快任务完成速度,比如批量文件处理等场景 。


 提出以下问题
 
1. 为什么 fork 要给子进程返回0,给父进程返回子进程 pid 
 
- 子进程返回0:子进程通过返回0能方便地知道自己是子进程,因为0是一个特殊值,与任何有效的进程ID都不同。这样子进程可以在后续代码中根据这个返回值来执行特定于子进程的逻辑。
- 父进程返回子进程 pid :父进程得到子进程的 pid 后,就可以通过这个 pid 来对特定的子进程进行管理和控制,比如等待子进程结束、向子进程发送信号等。同时,父进程可以同时创建多个子进程,通过不同的 pid 来区分和处理各个子进程。
 
2. 一个函数是如何做到返回两次的
 
 fork 函数会创建一个新的进程,即子进程。子进程是父进程的一个副本,它从 fork 函数调用处开始执行,就好像是父进程的一个“分身”。在 fork 函数执行时,内核会为子进程分配资源,并复制父进程的上下文,包括程序计数器、寄存器等。因此,看起来 fork 函数在父进程和子进程中都被执行了一次,也就有了两次返回。一次是在父进程中继续执行,返回子进程的 pid ;另一次是在子进程中执行,返回0。

二者返回存在顺序问题,我们无法表象过去,个人认为父进程优先于子进程返回,因此先输出父进程内容,后输出子进程内容。
 
3. 一个变量怎么会有不同的内容?如何理解?
 
在 fork 之后,父进程和子进程拥有各自独立的地址空间。虽然它们在 fork 之前的变量值是相同的,但 fork 之后,对一个进程中变量的修改不会影响到另一个进程中的同名变量。这是因为它们的地址空间是相互独立的,每个进程都有自己的一份变量副本。例如,父进程中的一个全局变量 x ,在 fork 之后,父进程和子进程都有自己的 x ,父进程修改自己的 x 不会影响到子进程的 x ,反之亦然。


 fork 后父子进程代码共享、数据单独开辟,主要有以下原因:
 
代码共享(写时拷贝
 
- 提高内存利用效率:程序代码通常是只读的,父子进程执行相同的程序代码,若各自复制一份到内存,会浪费大量空间。共享代码段能让多个进程共用同一份代码,减少内存占用。
- 保证代码一致性:共享代码可确保父子进程执行的代码完全一致,避免因代码复制产生不一致或错误。
 
数据单独开辟
 
- 进程独立性要求:每个进程需有独立数据空间,保证其数据操作不影响其他进程。父子进程后续可能执行不同任务,对数据有不同修改需求,若数据不独立,一个进程对数据的修改会影响另一个进程,导致程序逻辑混乱。
- 数据安全与稳定:单独开辟数据空间为进程提供了安全稳定的运行环境。一个进程的数据损坏或异常不会波及其他进程,增强了系统的稳定性和可靠性。
4.  fork 函数究竟是什么,干什么
 
 fork 函数是UNIX和类UNIX系统中的一个系统调用,用于创建一个新的进程。它的主要作用是将当前进程(父进程)复制一份,生成一个新的进程(子进程)。子进程在许多方面与父进程相似,包括程序代码、数据段、堆、栈等,但它们是两个独立的进程,有各自的进程控制块(PCB)和独立的地址空间。这样,父进程和子进程可以并发执行,各自执行不同的任务,从而实现多任务处理。例如,一个服务器程序可以通过 fork 函数创建多个子进程来同时处理多个客户端的请求。

操作系统学科进程状态(运行,阻塞,挂起)

进程状态与内存管理
 
在Linux系统中,进程状态丰富多样且与内存管理紧密相连。当进程处于阻塞状态时,往往是在等待特定事件,像从键盘读取数据。此时,进程会被挂起,其关键信息存储于 struct task_struct 结构体中。若操作系统内存资源严重匮乏,部分进程将被换出至交换分区(swap),以此节省内存,保障系统正常运转。待条件满足,比如所需数据准备就绪,进程又会被换入内存继续执行。
 
 struct task_struct 是Linux内核用于描述进程的重要结构体,涵盖进程状态、优先级等众多属性。而 struct dev 则用于描述设备,包含设备类型、状态,以及指向相关进程的指针等信息。部分设备结构体还设有等待队列,用于管理那些等待该设备资源的进程。
 
进程调度与运行
 
系统中存在一个由调度器管理的运行队列( struct runqueue ) ,该队列有指向队头和队尾进程的指针。调度器的职责是从运行队列里挑选合适的进程,使其能在CPU上运行。进程进入运行队列,意味着它已准备就绪,随时可被调度。
 
进程在CPU上并非一直运行至结束才释放资源,而是引入了时间片的概念(如图中示例的10ms )。当进程的时间片耗尽,即便尚未执行完毕,也会被调度器从CPU上移除,排至运行队列末尾,等待下一轮调度。这种频繁的进程上下操作,即进程切换,让所有进程的代码在一个时间段内都能得以执行,达成并发执行的效果。比如,即便进程遭遇 while(1); 这样的死循环代码,时间片机制也能确保其他进程有机会获得CPU调度。
 
补充完善
 
进程状态转换十分复杂,除阻塞和运行状态外,还有就绪状态、睡眠状态等。就绪状态的进程已万事俱备,只待CPU资源,一旦CPU空闲且被调度器选中,便会转入运行状态。睡眠状态又细分为可中断睡眠与不可中断睡眠,前者可被信号唤醒,后者一般在等待特定I/O操作完成后才会苏醒。
 
调度器选择进程的依据是不同的调度算法。常见的有先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(RR)、优先级调度等。Linux系统采用的完全公平调度器(CFS)更为复杂高效,它通过统计和比较进程的虚拟运行时间,公平分配CPU时间,防止某些进程长时间得不到调度。
 
内存管理方面,当内存资源不足时,操作系统在选择换出进程至交换分区时,会综合考量进程活跃度、内存使用量等因素。通常,不活跃进程更易被换出。进程换入内存时,需兼顾内存空间分配与进程需求,保障系统高效运行。此外,操作系统还借助页缓存等缓存机制,降低进程换入换出的性能开销,提升数据访问速度。


网站公告

今日签到

点亮在社区的每一天
去签到