系统调用指令描述pause挂起信号nice改变分时进程的优先级ptrace进程跟踪kill向进程发送信号pipe创建管道mkfifo创建 fifo 的特殊文件(命名管道)sigaction设置对指定信号的处理方法msgctl消息控制操作semctl信号量控制
Linux 进程和线程的实现Linux 进程在 Linux 内核结构中,进程会被表示为 任务,通过结构体 structure 来创建 。不像其他的操作系统会区分进程、轻量级进程和线程,Linux 统一使用任务结构来代表执行上下文 。因此,对于每个单线程进程来说,单线程进程将用一个任务结构表示,对于多线程进程来说,将为每一个用户级线程分配一个任务结构 。Linux 内核是多线程的,并且内核级线程不与任何用户级线程相关联 。
对于每个进程来说,在内存中都会有一个 task_struct 进程描述符与之对应 。进程描述符包含了内核管理进程所有有用的信息,包括 调度参数、打开文件描述符等等 。进程描述符从进程创建开始就一直存在于内核堆栈中 。
Linux 和 Unix 一样,都是通过 PID 来区分不同的进程,内核会将所有进程的任务结构组成为一个双向链表 。PID 能够直接被映射称为进程的任务结构所在的地址,从而不需要遍历双向链表直接访问 。
我们上面提到了进程描述符,这是一个非常重要的概念,我们上面还提到了进程描述符是位于内存中的,这里我们省略了一句话,那就是进程描述符是存在用户的任务结构中,当进程位于内存并开始运行时,进程描述符才会被调入内存 。
进程位于内存被称为 PIM(Process In Memory) ,这是冯诺伊曼体系架构的一种体现,加载到内存中并执行的程序称为进程 。简单来说,一个进程就是正在执行的程序 。进程描述符可以归为下面这几类
- 调度参数(scheduling parameters):进程优先级、最近消耗 CPU 的时间、最近睡眠时间一起决定了下一个需要运行的进程
- 内存映像(memory image):我们上面说到,进程映像是执行程序时所需要的可执行文件,它由数据和代码组成 。
- 信号(signals):显示哪些信号被捕获、哪些信号被执行
- 寄存器:当发生内核陷入 (trap) 时,寄存器的内容会被保存下来 。
- 系统调用状态(system call state):当前系统调用的信息,包括参数和结果
- 文件描述符表(file descriptor table):有关文件描述符的系统被调用时,文件描述符作为索引在文件描述符表中定位相关文件的 i-node 数据结构
- 统计数据(accounting):记录用户、进程占用系统 CPU 时间表的指针,一些操作系统还保存进程最多占用的 CPU 时间、进程拥有的最大堆栈空间、进程可以消耗的页面数等 。
- 内核堆栈(kernel stack):进程的内核部分可以使用的固定堆栈
- 其他: 当前进程状态、事件等待时间、距离警报的超时时间、PID、父进程的 PID 以及用户标识符等
当执行 fork 系统调用时,调用进程会陷入内核并创建一些和任务相关的数据结构,比如内核堆栈(kernel stack) 和 thread_info 结构 。
关于 thread_info 结构可以参考这个结构中包含进程描述符,进程描述符位于固定的位置,使得 Linux 系统只需要很小的开销就可以定位到一个运行中进程的数据结构 。
https://docs.huihoo.com/doxygen/linux/kernel/3.7/arch_2avr32_2include_2asm_2thread__info_8h_source.html
进程描述符的主要内容是根据父进程的描述符来填充 。Linux 操作系统会寻找一个可用的 PID,并且此 PID 没有被任何进程使用,更新进程标示符使其指向一个新的数据结构即可 。为了减少 hash table 的碰撞,进程描述符会形成链表 。它还将 task_struct 的字段设置为指向任务数组上相应的上一个/下一个进程 。
task_struct : Linux 进程描述符,内部涉及到众多 C++ 源码,我们会在后面进行讲解 。从原则上来说,为子进程开辟内存区域并为子进程分配数据段、堆栈段,并且对父进程的内容进行复制,但是实际上 fork 完成后,子进程和父进程没有共享内存,所以需要复制技术来实现同步,但是复制开销比较大,因此 Linux 操作系统使用了一种 欺骗 方式 。即为子进程分配页表,然后新分配的页表指向父进程的页面,同时这些页面是只读的 。当进程向这些页面进行写入的时候,会开启保护错误 。内核发现写入操作后,会为进程分配一个副本,使得写入时把数据复制到这个副本上,这个副本是共享的,这种方式称为 写入时复制(copy on write),这种方式避免了在同一块内存区域维护两个副本的必要,节省内存空间 。
推荐阅读
- .so Linux下动态库和静态库(.a) 的区别
- Linux中/etc/passwd配置文件详解
- linux后台开发中避免僵尸进程的方法总结
- 用 testdisk 恢复 Linux 上已删除的文件
- 使用 GNU bc 在 Linux Shell 中进行数学运算
- 谈谈如何学习Linux
- Redis源码剖析之SDS
- 如何获取Linux或者macOS系统版本相关信息
- linux异步IO编程实例分析
- Linux和Windows两种风格的操作系统,创建线程的方式有何不同?