2024-09-19 进程状态

一、进程的状态、优先级

一、进程的状态

CPU切换和运行的速度非常快

1. 并发和并发

  • 并行:多个进程在多个CPU下分别同时运行
  • 并发:CPU执行进程代码,不是把进程代码执行完毕才开始执行下一个,而是给每一个进程预分配一个时间片,基于时间片进行调度轮转(单CPU下)

2. 时间片

民用级别的操作系统——分时操作系统(调度任务尽量追求公平)
与之相对的就是实时操作系统

3. 进程具有独立性(详见专栏上篇文章)

4. 等待的本质

等待的本质:连入目标外部设备,CPU不调度

在操作系统中,每个CPU都有一个运行队列的结构体(struct),该结构体包含任务结构体(task struct),并链接所有已加载的进程控制块(PCB),形成基于链式结构的先进先出调度队列(struct runqueue),CPU通过该队列执行时间片轮转调度。

只要进程在运行队列中,该进程就叫做运行状态(已经准备好了,可以随时被CPU调度)

运行和阻塞的本质是让不同的进程处在不同的队列中

#define RUNNING 1
#define BLOCK 2
struct task_struct {
	int status;
};

阻塞挂起(内存资源严重不足的时候):内存与磁盘(swap 分区:用时间换空间,为了效率可以禁掉)换入换出

二、Linux的进程状态

在Linux内核源码中,进程的状态主要通过task_struct结构体中的state字段来描述。task_struct是内核中用于表示每个进程(任务)的核心数据结构,定义在include/linux/sched.h文件中。

task_struct中的state字段

task_struct结构体中的state字段用于表示进程当前的状态。这个字段通常是一个位掩码(bitmask),允许进程同时处于多个状态。常见的状态包括:

  • TASK_RUNNING: 进程正在运行或准备运行。
  • TASK_INTERRUPTIBLE: 进程处于可中断的睡眠状态,等待某个事件发生。
  • TASK_UNINTERRUPTIBLE: 进程处于不可中断的睡眠状态,通常用于等待关键资源。

深度睡眠,不可被杀掉

  • __TASK_STOPPED: 进程被停止(例如,收到SIGSTOP信号)。

进程做了非法但不致命的操作,被OS暂停了

  • __TASK_TRACED: 进程被跟踪(例如,被调试器调试)。

当进程被追踪时,遇到断点,此时状态为t

  • EXIT_DEAD: 进程即将退出,但其子进程尚未被收割。
  • EXIT_ZOMBIE: 进程已经终止,等待父进程收割其退出状态。

进程为什么会被创建?为了完成用户的任务
通过进程执行的结果,告知父进程/操作系统任务完成的情况
维持退出信息,方便父进程和操作系统查询

这些状态通过位掩码的方式组合使用,例如,一个进程可能同时处于TASK_INTERRUPTIBLE__TASK_STOPPED状态。

ps ajx | grep myprocess 查询 myprocess 进程,其中 STAT 中 S+ 代表进程处于可中断睡眠状态并且位于前台进程组S 则仅表示进程处于可中断睡眠状态。


三、练习题目

967

关于 linux 的进程,下面说法不正确的是:
A.僵尸进程会被 init 进程接管,不会造成资源浪费;
B.孤儿进程的父进程在它之前退出,会被 init 进程接管,不会造成资源浪费;
C.进程是资源管理的最小单位,而线程是程序执行的最小单位。Linux 下的线程本质上用进程实现
D.子进程如果对资源只是进行读操作,那么完全和父进程共享物理地址空间。

【A】

  • 僵尸进程是指子进程结束后,父进程没有调用wait()waitpid()来回收子进程的资源,导致子进程的进程描述符(PCB)仍然存在于系统中。
  • 僵尸进程会占用系统进程表项等资源,造成资源浪费。虽然最终会被init进程接管(init进程会周期性地等待其已有的子进程,回收僵尸进程的资源),但在被回收之前是会浪费资源的,所以选项A说法不正确。

【B】

  • 孤儿进程是指父进程先于子进程结束,此时子进程会被init进程(进程ID为1)收养。
  • init进程会负责回收孤儿进程的资源,不会造成资源浪费,选项B说法正确。

【C】

  • 在Linux系统中,进程是资源分配的最小单位,它拥有独立的地址空间、文件描述符等资源。线程是程序执行的最小单位,一个进程可以包含多个线程,这些线程共享进程的地址空间等资源。
  • Linux下的线程本质上是用轻量级进程实现的,它与进程有很多相似之处,选项C说法正确。

【D】

  • fork系统调用通过复制父进程创建一个子进程,父子进程数据独有,代码共享(在数据不发生改变的情况下父子进程资源指向同一块物理内存空间)。为了提高效率,Linux 使用写时复制(Copy-On-Write, COW)策略:在 fork() 之后,父子进程共享相同的物理页面,只有当其中一个进程尝试写入这些页面时,才会为该进程创建新的页面副本。

969

在抢占式多任务处理中,进程被抢占时,哪些运行环境需要被保存下来?[多选]
A.所有cpu寄存器的内容
B.全局变量
C.页表指针
D.程序计数器

【A】当进程被抢占时,CPU 寄存器的内容必须保存。因为寄存器中存储了进程当前的运行状态,如果不保存寄存器内容,当进程再次获得 CPU 时间片时,将无法从上次中断的位置正确执行,因为其运行环境已经丢失了关键的寄存器数据。
【B】全局变量通常存储在进程的地址空间中的数据段或BSS段内,并不会因为进程调度而改变。因此,它们不需要特别保存,因为它们在进程重新获得CPU时仍然会存在并且内容不变。
【C】每个进程都有自己的虚拟地址空间,由页表来管理。页表指针指向当前进程的页表,对于维护正确的内存映射至关重要。因此,在上下文切换时保存页表指针是必要的。
【D】程序计数器(PC)指示了下一条要执行的指令的位置。为了确保进程可以在被抢占的地方继续执行,必须保存程序计数器的值。
[ACD]

976

下列有关进程的说法中,错误的是? [多选]
A.进程与程序是一一对应的
B.进程与作业是一一对应的
C.进程是静态的
D.进程是动态的过程

2205

进程创建:
通过父子进程的返回值, 区分父子进程执行的逻辑,重点是理解子进程为什么从fork函数调用之后开始执行。
在Unix/Linux系统中,fork() 系统调用用于创建新进程。新创建的进程被称为子进程,而调用 fork() 的原有进程称为父进程。通过 fork() 创建子进程的过程以及其返回值是理解进程创建机制的关键。

fork() 创建子进程的过程

当一个进程调用 fork() 时,操作系统会执行以下步骤:

  1. 分配一个新的进程描述符(即新的进程控制块PCB)给子进程。
  2. 复制父进程的地址空间(包括数据段、堆栈等),使得子进程有一份与父进程相同的数据副本。注意,在现代操作系统中,这通常通过“写时复制”(Copy-On-Write,
    COW)技术实现,以提高效率。
  3. 设置子进程的初始状态:例如,设置程序计数器指向 _start 函数(C/C++程序入口点之前的启动代码),这样子进程会在下一次调度时从头开始执行程序;但实际上,由于 fork() 的特性,子进程将从
    fork() 调用之后的地方开始执行。
  4. 将新创建的子进程加入到就绪队列中,等待CPU时间片来执行。
  5. 返回两次:一次是在父进程中返回子进程ID(PID),另一次是在子进程中返回0。

fork() 的返回值

  • 父进程中,fork() 返回的是新创建的子进程的进程ID (PID)。父进程可以使用这个PID来识别和操作子进程。
  • 子进程中,fork() 返回0。这是因为子进程不需要知道自己的PID(它可以通过调用 getpid() 来获取),而是需要知道它是由哪个父进程创建的,因此返回0表示它是新创建的子进程。

区分父子进程执行逻辑

基于上述返回值,可以在调用 fork() 后使用条件语句来区分父子进程,并执行不同的逻辑:

pid_t pid = fork();

if (pid < 0) {
   // 错误处理:fork失败 } 
else if (pid == 0) {
   // 子进程的代码逻辑
    printf("这是子进程,我的PID是 %d\n", getpid()); }
else {
    // 父进程的代码逻辑
    printf("这是父进程,我刚创建的子进程的PID是 %d\n", pid);
}

子进程为什么从 fork() 调用之后开始执行

实际上,子进程并不是真正意义上从 fork() 调用之后才开始执行。子进程继承了父进程调用 fork()时的状态,这意味着它有父进程在 fork() 时刻的所有内存内容的副本。然而,因为 fork()的返回值不同,子进程中的程序流会在检查到 pid == 0 时走子进程特有的逻辑分支,从而看起来像是从 fork()调用之后开始执行。换句话说,子进程继续执行父进程中 fork()调用之后的代码,但由于它有自己的独立环境,所以它可以独立地执行这段代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值