写时拷贝
传统的fork系统调用直接把所有的资源复制给新创建的进程,但是这种实现过于简单,效率低下,因为并不支持拷贝数据的共享。
更糟的是如果新进程打算立即执行一个新的映像那么所有的拷贝都将前功尽弃。
Linux下面的fork采用的是写时拷贝的方法,也就是说让父进程与子进程拥有同一份拷贝,之后如果发生了写入数据的情况。再也根本不会被写入的情况下,它就不会复制了。例如直接调用了exec()
vfork函数其实在系统调用的时候步骤和fork一致但是,它不会拷贝父进程的页表项,子进程作为父进程的一个单独的线程执行。子进程运行在自己的内存空间内,父进程会在它执行的时候阻塞,知道子进程退出或执行exec
创建进程的详细步骤
fork函数是通过clone系统来完成创建进程的。这个调用来标识一下父进程和子进程之间共享的资源。
调用dup_task_struct()来创建一个内核栈,thread_info,task_struct等结构,一开始的时候其实父进程和子进程里面的资源和里都是一样的,此时子进程与父进程的进程描述符完全一致
check 检查当前进程是否已经超过了系统的最大文件描述符
清除子描述符里面一些内容,使它与父进程分开。但是进程描述符不需要去改变就是直接复制就好了,因为主要起的是描述作用,而不是记录的作用
设置标志位,TASK_UNINTERRUPTIBLE 来保证该进程不会被投入使用
copy-flags()用来更新struct_task的一些flags成员,
get_pid()给进程一个pid号
根据传递给clone的参数标志,copy_process函数拷贝或共享打开文件,文件系统信息等、信号处理函数、进程地址空间和命名空间
让父进程和子进程平分剩余的时间片
最后copy_process会返回一个指向子进程的指针
线程在Linux内部的实现
其实大体上与进程差不多也就有一下几点区别
同一程序内共享内存地址空间运行一组线程,这些线程可以共享打开的文件和其他资源,支持并发程序设计技术(多处理器)
线程父子共享内存空间,文件系统资源,文件描述符信号处理程序等。
内核线程
内核线程没有独立的地址空间,只在内核运行,并且只能从来不切换到用户空间去。
可以被调度和抢占
只能被其他的内核线程创建
进程终结
设置task_struct成员为PF_EXITING
使用del_tim_sync删除该进程的内核定时器
放弃内存使用的mm_struct,如果没有其他进程使用就彻底的放弃它(没有人在和它共享资源)
在IPC队列中删除掉这个进程的存在
递减文件描述符,文件系统数据、进程空间名字和信号处理函数的引用计数
将任务退出代码发给父进程存放
向父进程发送信号,将子进程的父进程重新设置为线程组中的其他线程或者调用init,把进程设置为TASK_ZOMBIE
切换到其他进程