7.15 Linix进程

less than 1 minute read

进程基础

  • 进程是操作系统分配资源的基本单位, 也是程序执行过程的实体。程序是代码和数据的集合,本身是一个静态的概念,而进程是程序的一次执行的实体,是一个动态的概念
  • 进程描述符(为了管理进程,内核需要对每个进程的属性和所需要做的事情,进行清楚的描述, 即task_struct)
  • task_struct数据结构
    • state: 描述进程状态
    • thread_info: 进程的基本信息
    • mm: mm_struct指向内存区描述符的指针
    • tty: tty_struct终端相关的描述符
    • fs: fs_struct当前目录
    • files: files_struct指向文件描述符的指针
    • signal: signal_struct所接收的信号描述
  • Linux进程所需具备的四要素: 1). 程序代码。代码不一定是进程专有,可以与其它进程共用; 2). 系统堆栈空间,这是进程专用的; 3). 在内核中维护相应的进程控制块。只有这样,该进程才能成为内核调度的基本单位,接受调度。并且,该结构也记录了进程所占用的各项资源; 4). 有独立的存储空间,表明进程拥有专有的用户空间
    • 如果缺少第四条,那么就称其为“线程”。如果完全没有用户空间,称其为“内核线程”;如果是共享用户空间,则称其为“用户线程”
  • 内核线程:没有独立的地址空间,即mm指向NULL。这样的线程只在内核运行,不会切换到用户空间。 所有内核线程都是由kthreadd作为内核线程的祖师爷,衍生而来的。

  • 进程创建过程
    • 初始化进程描述符
    • 申请相应的内存区域
    • 设置进程、加入调度队列
  • 创建进程系统调用, fork(), vfork(), clone()
    • clone(): 最基础的创建进程的系统调用,可以指明子进程的基础属性(由各种FLAG标识)、堆栈等等。
    • fork(): 通过clone()实现,它的堆栈指向的是父进程的堆栈,因此父子进程共享同一个用户态堆栈。fork的子进程需要完全copy父进程的内存空间,但是得益于写时复制技术,这个过程其实挺快。
    • vfork(): 也是基于clone()来实现的,是历史上对fork()的优化,因为fork()需要copy父进程的内存空间,并且fork()后常常执行execve()将另一个程序加载进来。因此vfork()实现时,会指明flag告诉clone()共享父进程的虚拟内存空间,以加快进程的创建过程。
  • 上下文切换: 进程创建好之后,内核必须有能力挂起正在CPU运行的进程,并切换其他进程到CPU上执行。
    • 硬件上下文切换: 主要通过汇编指令far jmp操作,将一个进程的描述符指针,替换为另一个进程描述符指针,并改变 eip、cs、esp等寄存器,从而改变程序的执行流
    • 软件上下文切换: 1). 内存地址的切换,切换页全局目录,安装新的地址空间; 2). 内核态堆栈的切换
  • fork()流程图

图片引用自Linux进程管理(二)--fork

进程应用

  • 进程间通信
    • 信号量: 不能传递复杂消息,只能用来同步
    • 管道: 容量有限速度较慢,只有父子进程能通讯
    • socket
    • 共享内存: 速度快,可以控制容量大小,但需要进行同步操作
    • 消息队列: 容量受到系统限制,有队列的特性,先进先出
    • 信号

参考