深入理解Linux内核 chap 3 进程

1. 进程, 轻量级进程和线程

  1. 通常, 我们把进程定义为程序执行的一个实例
  2. 从内核的观点来看, 进程就是用来担当分配系统资源(cpu时间, 内存)的实体
  3. 大部分的多线程应用程序, 一个进程有多个用户线程组成, 每个线程代表进程的一个执行流, 通过pthread (POSIX thread)库的标准库函数集编写。 从内核角度来看, 这些多线程应用程序, 仅仅只是一个普通的进程, 程序中多个执行流的创建, 处理, 调度整个都是在用户态中进行的。他的缺点是, 线程阻塞的话, 进程也就被阻塞了。
  4. Linux 中, 可以采用轻量级进程, 来对多线程应用程序提供更好的支持。两个轻量级进程基本上可以共享一些资源, 如地址空间, 打开的文件等。可以通过将轻量级进程和每个线程关联起来的方式, 进行实现

2. 进程描述符

  1. 每个进程描述符都是task_struct类型结构, 他的字段包含了一个与进程相关的所有信息
    这里写图片描述

2.1 进程状态

  1. 使用 state 字段可以描述进程当前所处的状态

2.2 标志一个进程

  1. 一般来说, 能够被独立调度的每个执行上下文都必须拥有他自己的进程描述符, 有自己的task_struct 结构
  2. 进程和进程描述符之间存在非常严格的一一对应关系, 内核对进程的大部分引用是通过进程描述符指针的形式进行的
  3. 使用进程标识符pid标识进程, 并且内核通过一个pidmap-array位图来表示当前已分配和闲置的pid号。
  4. Linux 把不同pid与系统中每个进程或者轻量级进程相关联, 提供最大的灵活性。
  5. 线程组, 一个线程组中所有的线程使用和该线程组领头线程相同的PID, 被存入进程描述符的tgid字段中。

2.2.1 进程描述符处理

  1. linux 通过把两个不同的数据结构(内核态的进程堆栈, 和 thread_info 线程描述符)紧凑的存放在一个单独为进程分配的存储区域内, 提升效率。
    这里写图片描述

2.2.2 标志当前进程

  1. 将这两个数据结构,紧密的结合在一起, 可以使得内核很容易的从esp寄存器的值获取得到当前在cpu上正在运行的进程的 thread_info结构地址。

2.2.3 双向链表

这里写图片描述

2.2.4 进程链表

借助上面提到的双向链表将进程联系在一起。

2.2.5 TASK_RUNNING状态的进程链表

  1. Linux 2.6 内核实现的运行队列, 为了让调度程序能够在固定时间内选出“最佳”可运行程序, 而建立了多个可运行进程链表, 每种进程优先权对应一个不同的链表
  2. 进程优先权范围 0 ~ 139, 因而运行队列被拆成了 140 个不同运行级别的队列

2.3 进程间关系

2.3.1 pidhash 表及链表

  1. 在很多情况下, 内核必须能够从进程的PID中导出对应进程的描述符指针。为了加速查找的过程, 我们引入了4个散列表(为了处理不同类型PID的字段 pid, tgid, pgrp, session)
    这里写图片描述

2.4 如何组织进程

2.4.1 等待队列

  1. 等待队列实现了在某个事件上的条件等待, 希望等待特定事件的进程把自己放进合适的等待队列, 并放弃控制权。 因而, 等待队列表示一组睡眠的进程。
  2. 引入了两个数据结构:
// 等待队列头
struct _ _wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct _ _wait_queue_head wait_queue_head_t;

// 等待队列中的元素
struct _ _wait_queue {
unsigned int flags;
struct task_struct * task;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct _ _wait_queue wait_queue_t;

通过等待队列头中的lock 自旋锁保证同步, 元素中的flag 标记是否为互斥进程, func回调函数。

2.4.2 等待队列的操作

互斥进程, 一次只能唤醒一个, 非互斥的进程, 可以唤醒多个

2.5 进程资源限制

3. 进程切换

3.1 硬件上下文

  1. 每个进程有自己的地址空间, 但是所有的进程必须共享CPU 寄存器, 因而, 在恢复一个进程执行之前, 内核必须保证每个寄存器装入了挂起进程的值
  2. 而必须装入寄存器的一组数据被称为硬件上下文, 进程硬件上下文的一部分放在TSS 段, 而剩余部分存放在内核态的堆栈中。

3.2 任务状态段

  1. 称为 TSS段

3.2.1 thread 字段

每个进程描述符中的thread_struct结构的thread 字段, 用来存放硬件上下文

3.3 执行进程切换

  1. 进程切换, 发生在 schedule 函数上, 分为两个步骤:
    • 切换页全局目录来安装一个新的地址空间
    • 切换内核态堆栈和硬件上下文

3.4 保存和加载FPU,MMU及XMM寄存器

  1. 8086cpu 不会再tss 中自动保存 FPU(浮点计算单元), MMX 和 XMM 寄存器信息, 利用 TS 标记, 在真正需要的时候, 实现对这些寄存器的保存和恢复操作。

4. 创建进程

  1. 写时复制技术, 提升进程创建的 效率。

4.1 clone, fork, vfork

底层都是通过clone实现

4.2 内核线程

  1. 不受不必要的用户态上下文的拖累, 效率高
  2. 内核线程只运行在内核态, 而普通进程可以在内核态运行, 也可以在用户态运行

5. 撤销进程

5.1 进程终止

  1. exit, exit_group

5.2 进程删除

  1. Unix 允许进程查询内核获取他的父进程的PID, 或者任何子进程的执行状态
  2. 因此, 不允许Unix 内核在进程一终止后就丢弃包含在进程描述符字段中的数据, 只有父进程发出了与被终止的进程相关的wait 类系统调用之后, 才允许这样做。
  3. 僵死状态: 技术上, 进程已经死掉了, 但是必须保存他的描述符, 直到父进程得到通知。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值