一、进程
2、分配进程描述符
- 每个任务的thread_info 结构在色的内核栓的尾端分配。结构中task 域中存放的是指向该任务实际task_struct 的指针。
- 进程描述符的存放内核通过一个唯一的进程标识值(process identification value)或PID 来标识每个进程。PID 是一个数,表示为pid_t 隐含类型,实际上就是一个int类型。
- 在内核中,访问任务通常需要获得指向其task_struct 的指针,有的硬件体系结构可以拿出一个专门寄存器来存放指向当前进程task_struct的指针,用于加快访问速度。
3、进程状态进程描述符中的state域描述了进程的当前状态。该域的值也必为下列五种状态标志之:
4、进程上下文当一个程序调执行了系统调用或者触发了某个异常,它就陷入了内核空间。此时,我们称内核“代表进程执行”并处于进程上下文中。
5、进程家族树所有的进程都是PID 为1 的init 进程的后代。内核在系统启动的最后阶段启动init 进程。该进程读取系统的初始化脚本( initscript)并执行其他的相关程序,最终完成系统启动的整个过程。系统中的每个进程必有一个父进程, 相应的,每个进程也可以拥有零个或多个子进程。拥有同一个父进程的所有进程被称为兄弟。每个task_struct 都包含一个指向其父进程tast_struct、叫做parent 的指针,还包含一个称为children 的子进程链表。所以,对于当前进程, 可以通过下面的代码在得其父进程的进程描述符:structtaskstruct*my_parent = current- >parent;对于给定的进程,获取链表中的下一个进程:list_entry(task->tasks.next , struct task_ s truct, tasks)在取前一个进程的方撞与之相同:list_ entry(task->tasks. prev, s truct task_ struct, tasks)
三、进程创建Unix
1、采用了与众不同的实现方式,分解到两个单独的函数中去执行:fork() 和exec().首先, fork()通过拷贝当前进程创建一个子进程。子进程与父进程的区别仅仅在于PID(每个进程唯一)、PPID (父进程的进程号,子进程将其设置为被拷贝进程的PID )和某些资源和统计量(例如,挂起的信号,它没有必要被继承)。exec()函数负责读取可执行文件并将其载入地址空间开始运行。3.3.1 写时拷贝传统的fork()系统调用直接把所有的资源复制给新创建的进程。Linux 的fork()使用写时拷贝( copy-on-write)页实现。
2、只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。fork()的实际开销就是复制父进程的页表以反给予进程创建唯一的进程描述符。
3、fork()Linux 通过clone()系统调用实现fork() 。这个调用通过一系列的参数标志来指明父、子进程需要共享的资源。fork()、vfork()和clone()库函数都根据各自需要的参数去调用clone(),然后由clone()去调用do_fork()。do_fork 完成了创建中的大部分工作,它的定义在kemeVfork.c 文件中。该函数调用copy_process()函数,然后让进程开始运行。
4、vfork()除了不拷贝父进程的页表项外, vfork()系统调用和fork()的功能相同。子进程作为父进程的一个单独的钱程在它的地址空间里运行,父进程被阻塞,直到子进程退出或执行exec()。vfork()系统调用的实现是通过向clone()系统调用传递一个特殊标志来进行的。
- 1)在调用copy_process()时, task_struct 的vfor_done 成员被设置为NULL 。
- 2)在执行do_fork()时,如果给定特别标志,则vfork_done 会指向一个特定地址。
- 3)子进程先开始执行后,父进程不是马上恢复执行,而是一直等待,直到子进程通过vfork_done 指针向它发送信号。
- 4) 在调用mm_release()时,该函数用于进程退出内存地址空间, 并且检查vfork_done 是否为空,如果不为空,则会向父进程发送信号。
- 5)回到do_fork(),父进程醒来并返回。
- clone(CLONE_VMclone_CLONE_FILBSCLONE_SIGHAND )
- 递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类
3、内核线程内核经常需要在后台执行一些操作。这种任务可以通过内核线程( kernel thread)完成一独立运行在内核空间的标准进程。
- 1)将tast_struct 中的标志成员设置为PF_EXITING
- 2)调用del_timer_ sync()删除任一内核定时器。根据返回的结果,它确保没有定时器在排队,也设有定时器处理程序在运行。
- 3)如果BSD 的进程记账功能是开启的, do_exit()调用acct_update_ integrals()来输出记账信息。
- 4)然后调用exit_mm()函数释放进程占用的mm_struct,如果没有别的进程使用它们(也就是说,这个地址空间没有被共享),就彻底释放它们。
- 5) 接下来调用sem_ exit()函数。如果进程排队等候IPC 信号,它则离开队列。
- 6)调用exit_files()和exit_fs(),以分别递藏文件描述符、文件系统数据的引用计数。如果其中某个引用计数的数值降为零,那么就代表没有进程在使用相应的资源,此时可以释放。
- 7)接着把存放在task_struct 的exit_code 成员中的任务退出代码置为由exit()提供的退出代码,或者去完成任何其他由内核机制规定的退出动作。退出代码存放在这里供父进程随时检索。
- 8)调用exit_notify()向父进程发送信号,给子进程重新找养父,养父为钱程组中的其他钱程或者为init 进程,并把进程状态(存放在task_struct 结构的exit_state 中〉设成EXIT_ZOMBIE 。
- 9) do_exit()调用schedule()切换到新的进程。因为处EXIT_ZOMBIE 状态的进程不会再被调度,所以这是进程所执行的最后一段代码。do_exit()永不返回。
- 1)它调用一exit_signal(),该函数调用_unhash proc(),后者又调用detach_pid()从pidhash上删除该进程,同时也要从任务列表中删除该进程。
- 2) _exit_sal()释放目前僵死进程所使用的所有剩余资源,并进行最终统计和记录。
- 3)如果这个进程是钱程组最后一个进程,并且领头进程已经死掉,那么release_task()就要通知僵死的领头进程的父进程.的release_task()调用put_task_ struct()辑放进程内核和read_info 结构所占的页,并释放tast_struct 所占的slab 高速缓存。