原文链接 : Anthony的简书博客
最近在阅读《Linux内核设计与实现》,这里做一下linux中进程相关的知识点整理,以及android中进程的浅析。
下面1,2小节整理自《Linux内核设计与实现》 第三章《进程管理》和第四章《进程调度》。第3节整理android中进程的知识点。
1 Linux中的进程管理
以下内容整理自:《Linux内核设计与实现》 第三章《进程管理》
1.1进程和线程
进程是资源分配的最小单位。
线程是操作系统调度执行的最小单位。
进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作(比如,创建,销毁等)都是由内核来实现的。Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程,线程是一种特殊的进程。
进程提供2种虚拟机制:虚拟处理器和虚拟内存
每个进程有独立的虚拟处理器和虚拟内存,每个线程有独立的虚拟处理器,同一个进程内的线程有可能会共享虚拟内存。
内核把进程的列表存放在任务队列(task list)中(双向循环链表),链表的每一项类型为task_struct,我们称之为进程描述符(process descriptor)。进程的信息主要保存在task_struct中(位于 include/linux/sched.h)
通过task_struct和thread_info存放和表示进程。
进程标识PID(process identification value)和线程标识TID(thread identification value)对于同一个进程或线程来说都是相等的。
Linux中可以用ps命令查看所有进程的信息:
ps -eo pid,tid,ppid,comm
1.2 进程的生命周期
进程的各个状态之间的转化构成了进程的整个生命周期。
进程有五种进程状态:
除了图片上面的三种还有,_TASK_TRACED
和_TASK_STOPPED
1.3 进程的创建
Linux中创建进程分2步:fork()和exec()。
1 fork(): 通过拷贝当前进程创建一个子进程 (实际上最终是通过clone( ) )
2 exec(): 读取可执行文件,将其载入到地址空间中运行 (是一个系统调用族)
创建的流程:
1 调用dup_task_struct()为新进程分配内核栈,task_struct等,其中的内容与父进程相同。
2 check新进程(进程数目是否超出上限等)
3 清理新进程的信息(比如PID置0等),使之与父进程区别开。
4 新进程状态置为 TASK_UNINTERRUPTIBLE
5 更新task_struct的flags成员。
6 调用alloc_pid()为新进程分配一个有效的PID
7 根据clone()的参数标志,拷贝或共享相应的信息
8 做一些扫尾工作并返回新进程指针
9 创建进程的fork()函数实际上最终是调用clone()函数。
创建线程和进程的步骤一样,只是最终传给clone()函数的参数不同。
比如,通过一个普通的fork来创建进程,相当于:clone(SIGCHLD, 0)
创建一个和父进程共享地址空间,文件系统资源,文件描述符和信号处理程序的进程,即一个线程:clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)
Linux通过Clone()系统调用实现fork()
在内核中创建的内核线程与普通的进程之间还有个主要区别在于:内核线程没有独立的地址空间,它们只能在内核空间运行。这与之前提到的Linux内核是个单内核有关。
1.4 进程的终止
发生在进程调用exit()系统调用时
和创建进程一样,终结一个进程同样有很多步骤:
子进程上的操作,靠do_exit()完成->定义于(kernel/exit.c)
1 设置task_struct中的标识成员设置为PF_EXITING
2 调用del_timer_sync()删除内核定时器, 确保没有定时器在排队和运行
3 调用exit_mm()释放进程占用的mm_struct
4 调用sem__exit(),使进程离开等待IPC信号的队列 <