7.15 Linix进程
进程基础
- 进程是操作系统分配资源的基本单位, 也是程序执行过程的实体。程序是代码和数据的集合,本身是一个静态的概念,而进程是程序的一次执行的实体,是一个动态的概念
- 进程描述符(为了管理进程,内核需要对每个进程的属性和所需要做的事情,进行清楚的描述, 即
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()
流程图
进程应用
- 进程间通信
- 信号量: 不能传递复杂消息,只能用来同步
- 管道: 容量有限速度较慢,只有父子进程能通讯
- socket
- 共享内存: 速度快,可以控制容量大小,但需要进行同步操作
- 消息队列: 容量受到系统限制,有队列的特性,先进先出
- 信号