现代操作系统 原理与实现(银杏书)—— 进程与线程 (纤程、协程)

进程与线程

进程

每个进程对应一个运行中的程序

进程的内存空间布局

  • 自顶向下依次(顶为最大地址,底为最小地址)为:
    • 内核代码及数据
      • 处在
    • 内核栈
    • 用户栈
      • 自顶向下扩展
      • 栈底在高地址,栈顶在低地址
    • 代码库(只读)
    • 用户堆
      • 自底向上扩展
      • 堆顶在高地址
    • 数据与代码段

进程控制块 PCB

Linux 4.14中PCB对应的数据结构task_struct包含的部分:

    struct task_struct {
        // 进程状态
        volatile long state;
        // 虚拟内存状态
        struct mm_struct *mm;
        // 进程标识符
        pid_t pid;
        // 进程组标识符
        pid_t tgid;
        // 进程间关系
        struct task_struct __rcu *parent;
        struct list_head children;
        // 打开的文件
        struct files_struct *files;
        // 其他状态(如上下文)
        ...
    }

进程操作

进程的创建:fork
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
  • 返回值:
    • 0: 子进程
    • 0:父进程

    • <0:出错
进程的执行:exec
#include <unistd.h>

int execve(const char *pathname, char *const argv[], char *const envp[]);
  • 当execve被调用时,操作系统完成以下:
    1. 将pathname的可执行文件的数据段和代码段载入当前进程的地址空间中
    2. 重新初始化堆和栈
    3. 将PC设置到可执行文件的入口点
进程树
  • init 进程
    操作系统的第一个进程
    之后所有的进程都是由他直接或间接创建出来的。
  • kthreadd 进程
    进程是第二个进程
    所有有内核创建和管理的进程都是由它fork出来的
  • login 进程
    最后由init创建出来,要求用户登录
    之后会从login进程fork出来bash进程
进程管理

僵尸进程

  • 子进程结束了,但是父进程没有回收(wait)
  • 内核会为僵尸进城保留其进程描述符(PID)和终止时信息(waitpid中的status)
    孤儿进程
  • 父进程退出后,子进程还在运行
  • 孤儿进程会被init进程(进程号为1)收养,并由init进程对他们完成状态收集工作。
进程组和会话
  • 进程组是进程的集合
    • task_struct中的tgid即为进程组标识符
    • 进程组的作用体现在对信号的处理上
    • 可以通过killpg想一个进程组发送信号,这个信号会发送给进程组内所有的进程。
  • 会话是进程组的集合
    • 会话和进程组主要用于shell环境中的进程管理。

fork过时了吗?

fork的优缺点

优点

  • 设计上简洁 —— 不需要传递任何参数
  • fork强调进程与进程之间的联系(父子进程)
    局限
  • fork的实现已经越来越复杂
  • fork的性能太差
    • fork需要创建出原进程的一份拷贝,内存拷贝效率太低
  • fork存在潜在安全漏洞
替代
  • 合二为一:posix_spawn
  • 限定场景:vfork
  • 精密控制:rfork/clone

NOTE clone 可以认为是fork的“精密控制”版本

线程

多线程的地址空间布局

重点 线程只有栈不同,其他区域都共享

对于同一个进程的多个线程:

  • 内核栈和用户栈分离
  • 其他区域共享

线程控制块TCB

  • 线程上下文context是上下文切换的基础。
  • 线程上下文主要指的是当前处理器中大部分寄存器的值。其中包括:
    • 程序计数器PC
    • 通用寄存器
    • 特殊寄存器
      存储CPU当前的一些硬件状态和配置,如页表地址等

线程操作

  • 以POSIX线程库为例
线程创建:pthread_create
#include <pthread.h>

int pthread_create(pthread_t *thread, 
					const pthread_attr_t *attr,
					void *(*start_routine) (void *),
					void *arg);
  • pthread_create是通过clone实现的

线程的本质

  • 通过一些参数,clone实际上创建出一个从属于原进程、与原进程共享大量数据结构、拥有私有栈的实例,而这个实例就对应一个线程。

纤程

目前,主流的操作系统都是一对一的线程模型,用户态和内核态线程具有一对一关系,可以认为用户态线程的执行几乎完全受到操作系统调度器的管理。
然而,1. 应用程序相比于操作系统调度器对线程的语义和执行状态更加了解,能够做出更优的调度策略;2. 用户态线程相比于内核态线程要更加轻量级、创建和切换的开销要低得多。

在这样的背景下

  • 操作系统开始提供更多对用户态与内核态多对一模型的用户态线程:即纤程(fiber)
  • 除操作系统外,程序语言也提供了对纤程的支持,称为:协程(coroutine)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值