进程是处于执行期的程序以及相关资源的总称。通常是fork()调用的结果。该系统调用通过复制一个进程来创建新的进程。fork()系统调用从内核返回两次,一次返回父进程,另一次返回新产生的子进程。接着调用exec()这组函数就可以创建新的地址空间,并将程序载入。最终通过exit()退出执行。
内核把进程的列表存放在task_list(任务队列)的双向循环列表里,每一项称为进程描述符(process descriptor),包含一个具体进程的所有信息。
linux通过slab分配器分配task_struct结构。
用slab分配器动态生成task_struct,只需在栈底(向下生成的栈)创建一个新的结构 struct thread_info.
通过current宏查找进程描述符。x86系统,current把栈指针的后13位有效位屏蔽掉,用来计算偏移,最后current再从thread_info的task域提取并返回task_struct的地址。
设置当前进程状态 set_task_state(task,state);
进程创建: 使用fork()和exec()函数。fork()通过拷贝当前进程来创建一个新的进程,和父进程唯一区别在于PID、某些资源和统计量不同;exec()负责读取可执行文件并载入地址空间开始运行。linux的fork()采用写时拷贝页实现,也就是说资源的复制只有在写入的时候才会被复制,在此之前,都是以只读的方式共享,这种技术使得地址空间的页的拷贝被推迟到实现发生写入的时候才进行。
fork()具体执行流程如下:
线程在linux中的实现:
在内核中,线程被看成一个普通的进程,每个线程都拥有一个唯一的task_struct。在同一程序内共享内存地址空间的一组线程还可以共享打开的文件和其他资源。
创建线程:
现成的创建和普通进程的创建类似,只不过需要在调用clone()传递一些参数标志来指明共享的资源。
内核线程:
内核经常需要在后台完成一些操作需要内核线程完成,内核线程是独立运行在内核空间的标准进程,它们没有独立的地址空间,只在内核空间运行,不会被切换到用户空间去。从现有内核进程创建一个新的进程方法如下:
内核线程启动后就一直运行知道调用do_exit()退出或者内核其他部分调用kthread_stop()退出。
进程终结:
主要分为以下两个阶段:
do_exit()主要做以下工作: