-
内核调度的对象不是进程而是线程
-
线程之间可以共享虚拟内存(感觉拥有系统的所有内存资源),但每个都拥有各自的虚拟处理器(感觉拥有自己的处理器)。
-
Linux中用fork()函数(借助clone()函数实现)来创建进程,执行完该函数后,父进程恢复执行,子进程开始执行。Fork()系统调用从内核中返回两次:1、回到父进程。2、回到新产生的子进程。
-
创建新进程后(来创建新的地址空间),调用exec()函数把新的程序载入其中。程序通过exit()系统调用退出执行后被设置为僵死状态,直到父进程调用wait()为止。
-
进程存放在任务队列的双向循环链表中,每一项是类型为task_struct(进程描述符)的结构(包含具体进程的所有信息)。
-
在2.6版之前,各进程的task_struct存放在各自内核栈的尾端。而在之后,Linux通过slab分配器分配task_struct结构,在各进程的内核栈的栈底或者栈顶创建一个新的结构struct thread_info。(避免使用额外的寄存器)
-
通过current宏查找当前正在运行进程的进程描述符。不同的硬件体系结构,该宏具有不同的实现。
-
通过set_current_state() 设置指定进程的指定状态。
-
当一个程序执行了系统调用或者触发某个异常,那么它就陷入了内核。系统调用和异常是对内核明确定义的接口。
-
系统启动的最后阶段启动init进程,该进程读取系统的初始化脚本并执行其它的相关程序。Parent的指针用于存放父进程task_struct,而子进程链表用于存放子进程的。
-
资源的复制(内核一开始并不复制整个地址空间)只有在需要写入的时候才进行,在此之前只是以只读的方式共享。因此fork()的实际开销只是复制父进程的页表以及给子进程创建唯一的进程描述符。一般在进程创建以后会马上在复制的地址空间运行一个而执行文件。
-
Fork | vfork |__clone->clone->do_fock->copy_process:首先调用duo_task_struct()创建内核栈、thread_info结构和task_struct结构。检查该用户所拥有的进程数是否超出限制,开始修改进程描述符内部的数据。设置子进程的状态为TASK_UNINTERRUPTIBLE,以保证不会运行。调用task_flags()以更新task_struct的flag成员(设置PF_SUPERPRIV(超级用户权限)和PF_FORKNOEXEC(还未调用exec函数))。调用alloc_pid()为进程分配一个有效的PID。最后扫尾并返回指向子进程的指针。
-
父进程调用fork后,创建成功后,父子进程同时从fork点开始执行。
-
Vfock和fork的区别在于vfork不拷贝父进程的页表项。且子进程作为父进程的一个单独线程在它的地址空间中运行,父进程被阻塞,知道子进程退出或者执行exec()。
-
每一个线程都有唯一的task_struct结构,而相对于其它系统,会有一个专门的包含有各线程指针的进程描述符,而linux仅仅给线程分配task_struct结构。
-
内核通过运行内核线程从而达到在后台执行一些操作的目的。内核线程没有独立的地址空间,且只是在内核空间中运行,内核线程只能由其它内核线程创建
-
通过调用do_wait()实现进程终结,进程终结之后系统还保留这它的进程描述符,最终会调用release_task()函数释放进程描述符等达到最终释放进程的目的。
-
孤儿进程会在退出时白白耗费内存,永远处于僵死状态(解决办法:do_exit函数中会调用exit_notify函数找一个线程作为父亲或者让init作为他们的父亲。)。在寻找父进程时,通过使用两个相对较小的链表减轻了遍历带来的消耗(在一个单独的被ptrace跟踪的子进程链表中搜索相关的兄弟进程)。
进程相关概念的理解
最新推荐文章于 2022-09-28 02:32:02 发布