linux进程管理(2)
进程家族树
在task_struct数据结构中,我们可以通过parent和children等域,如获得当前进程的父进程:
struct task_struct *my_parent = current->parent
而在Linux系统中,如前面所述,是保存在一个双向链表中的,所以可以通过以下方法获得前一个进程:
list_entry(task->tasks.next,struct task_struct, task)
同样可以通过next_task(task)和prev_task(task)这两个宏实现对前一个进程和后一个进程的获取,而实际上,for_each_process(task)宏提供了依次访问整个任务队列的功能,如
struct task_struct *task
for_each_process(task){
//做相应操作
}
进程的创建
如前面提到的,Linux系统中通过fork()函数实现进程的创建。Linux的fork()使用写时拷贝页实现。内核其实并不拷贝整个进程的地址空间,而是让父进程和子进程共享一个拷贝。只有在需要写入时,数据才会被复制,从而使各个进程拥有各自的拷贝。
Linux通过clone()系统调用实现fork()。fork()、vfork()和__clone()库函数都根据各自需要的参数标志去调用clone()。然后clone()去调用do_fork()。do_fork()完成进程创建的大部分工作,其定义见kernel/fork.h。该函数调用了copy_process()函数。而copy_process()是创建进程的主要函数:
1、调用dup_task_struct()函数为新进程创建一个内核栈、thread_info结构和task_struct,这些值与当前的进程的值相同。
2、 子进程的状态被设置为TASK_UNINTERRUPTIBLE以保证它不会被投入执行。
3、 copy_process()调用copy_flags()函数,以更新task_struct的flags成员。表明进程是否拥有超级用户权限的PF_SUPERPRIV标志被清0, 表明进程还没有调用exec()函数的PF_FORKNOEXE标志被设置。
4、 调用get_pid()为新进程获取一个PID.
5、 根据传递给clone()函数的参数,copy_process()拷贝或者共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间等。
6、 让父进程和子进程平分剩余的时间片。
7、 copy_process()返回一个指向子进程的指针。
vfork()函数和fork()基本相同,所不同的是不拷贝父进程页表项。vfork()系统调用的实现是通过向clone()系统调用传递一个特殊标志来进行:
1、在调用copy_process()函数时,task_struct的vfork_done被置为NULL
2、 在执行do_fork()函数时,如果给定特别标志,则vfork()会指向一个特殊地址。
3、 子进程开始执行后,父进程不是马上恢复执行,而是一直等待子进程通过vfork_done指针向它发送信号。
4、 在调用mm_release()时,该函数用于进程退出内存地址空间,并检查vfork_done是否为空,如果不为空,则向父进程发送信号。