接上一节
TASK_UNINTRRUPTIBLE不可中断等待状态
TASK_ZOMBIE僵死状态,进程已经结束,但资源没有释放
TASK_STOP进程停止执行
9 进程状态间的转换:
现存的进程调用fork()函数并创建新的进程,则进程进入TASK_RUNNING就绪但还未执行,当通过schedule()
函数调用context——switch()函数是进入TASK_RUUNING运行状态,在该状态下调用do_exit()函数时就会进入
TASK_ZOMBIE任务被终止,但是如果任务被优先级更高的任务抢占则进入TASK_RUNNING就绪状态,如果为了等待特定事件
,任务在等待队列上睡眠就会进入TASK_INTRRUPTIBLE或者TASK_UNINTRRUPTIBLE等待,在TASK_INTRRUPTIBLE或者TASK_UNINTRRUPTIBLE状态下,等待的事件发生后任务被唤醒并且被重新置入运行队列中而进入TASK_RUNNING就绪状态。
10 可以调用set_task_state(task,state)函数设置进程的状态。即执行task—>state=state
11 理解进程上下文:
当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的上下文,以便在再次执行该进程时,能够必得到切换时的状态执行下去。在LINUX中,当前进程上下文均保存在进程的任务数据结构中。在发生中断时,内核就在被中断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中继服务结束时能恢复被中断进程的执行。
12 在Linux系统中所有的进程都是PID为1的init进程的后代。
每个进程必有一个父进程。
每个进程可以有0个获多个进程。
拥有同一个父进程的所有进程被称为兄弟
每个task_struct都包含有一个指向其父进程task_struct叫做parent的指针还包含一个称为children的子进程的链表
任务队列是一个双向循环链表
13 list_entry(task->tasks.next,struct task_struct,tasks)获取下一个链表中的进程
list_entry(task->tasks.prev,struct task_struct,tasks)获取前一个链表中的进程
14 Linux的fork()使用写时拷贝页实现
fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符
15 Linux通过clone()系统调用实现fork(),然后由clone()去调用do_fork(),该函数的定义在kernel/fork.c中,该函数又调用copy_process()函数,然后让进程开始执行。
copy_process()函数执行的工作有:
....dup_task_struct()为新进程创建一个内核栈、thread_info结构和task_struct,这些值与父进程的值相同,子进程和父进程的描述符完全相同
....检查当前用户所拥有的进程数目没有超出分配给的资源限制
....子进程的进程描述符内的成员要被清0或者设置为初值
....子进程的状态设置为TASK_UNINTRRUPTIBLE以保证它不会投入运行
....调用copy_flags()更新task_struct的flags成员。表明进程是否拥有超级用户的权限PF_SUPERPRV=0,表明进程还没有调用exec()函数的PF_FORNOEXEC标志被设置
....调用get_pid()为新进程获取有效地PID
....根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间
....让父进程和子进程平分剩余的时间片
....返回一个指向子进程的指针
16 Linux把所有的线程都当做进程来实现,线程被视为一个与其他进程共享某些资源的进程
与进程的区别:内核线程没有独立的地址空间,只在内核空间运行,从来不切换到用户空间
内核线程创建一个新的内核线程的方法
int kernel_thread(int (*fn)(void *),void *arg,unsigned long flags)
17 进程的析构发生在它调用exit()之后,可以显示的调用,也可以隐式的从某个程序的主函数返回其将调用do_exit()函数
该函数完成的工作是:
。。。。将task_struct中的标志成员设置为PF_EXITING
。。。。调用del_timer_sync()删除任一内核定时器,以确保没有定时器在排队也没有定时器出来程序运行
。。。。如果BSD进程记账功能开启,则调用acct_process()输出记账信息
。。。。调用_exit_mm()放弃进程占用的mm_struct
。。。。调用exit_sem(),
。。。。调用_exit_files()、_exit_fs()、exit_namespace()和exit_sighand(),以分别递减文件描述符、文件系统数据、进程名字空间和信号处理的引用计数
。。。。把存放在task_struct的exit_code成员中的任务退出
。。。。调用exit_notify()向父进程发生信号,将子进程的父进程从新设置为线程组中的其他线程或init进程,并把进程状态设为TASK_ZOMBIE
。。。。最后调用schedule()切换到其他的进程
do_exit()的实现在kernel/exit.c文件中
18 调用release_task()释放进程描述符,主要完成:
。。。。调用free_uid()来减少该进程拥有者的进程使用计数
。。。。调用unhash_process()从pidhash上删除该进程,也要从task_list中删除该进程
。。。。如果进程正在被跟踪,release——task()将跟踪的进程的父进程重设为其最初的父进程并将它从ptrace_list删除
。。。。最后调用put_task_struct()释放进程的内核栈和thread_info结构所占页,并释放task_struct所占的slab高速缓存
19本章小结:了解进程的创建和删除过程,进程和线程的区别