Linux进程管理之问题

1、为什么调用fork()函数将返回两次?

这是因为在do_fork->copy_process->copy_thread函数中,将子进程的用户态堆栈的开始地址设置为父进程的用户态堆栈的开始地址,这样当父子进程从内核态返回到用户态的时候,返回的地址相同,这就解释了为什么fork一次却返回两次的原因。

2、为什么要在task_struct中设置mm和active_mm两个mm_struct成员呢?

这是由于内核线程没有用户态地址空间,所以它的mm设置为NULL,但是由于页目录的地址是保存在mm结构中的,从其他进程切换到这个内核态线程时,调度器可能需要切换页表,为此增加了一个active_mm,对于mm为NULL的内核态线程,就借用其他进程的mm_struct,也就是说把它的active_mm指向其他进程的mm结构,当进行进程切换时,统一使用active_mm就可以了。但是其他进程不是有自己独立的页表吗?由于内核态线程只使用内核地址空间,因此这不会有问题。

3、有如下说法:1.task_struct的mm成员用来描述3GB用户态虚拟地址空间;2.内核线程可以借用上一个调用的用户进程的mm中的页表来访问内核地址空间。如果是这样的话,那么task_struct的mm成员能不能描述1GB的内核地址空间?如果不能的话,为什么会有2这种说法?

task_struct的mm成员不能描述1GB的内核地址空间,只是因为mm成员中保存了页目录的信息pgd_t,而且所有进程共享1G的内核态地址空间,所以可以使用上一个用户进程的mm中的页表访问内核地址空间。

4、为什么所有进程共享1G的内核态地址空间?

因为fork()会复制当前进程的task_struct结构,同时会为新进程复制mm结构。此时当前进程的3GB~4GB的内核态虚拟地址对应的页表项(页目录项)被复制到进程的页表项(页目录项)中,所以说所有进程共享1G内核态地址空间。但是对于用户态虚拟地址区域,则把它的进程页表项(页目录项)设置为只读,这样当其中一个进程对其进行写入操作时,do_page_fault()会分配新的物理页面,并建立映射,从而实现COW机制。

5、父进程要求子进程退出时发送信号,那么父进程要求子线程退出时发送信号吗?为什么?

父进程不要求子线程退出时发送信号,这是因为子线程共享父进程的一些资源,所以不需要父进程来获取这些信息,也就不需要向父进程发送信号。这一点可以在do_fork->copy_process

                p->exit_signal=(clone_flags & CLONE_THREAD) ? -1 :(clone_flags &CSIGNAL);

以及do_exit->exit_notify

                if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {

                int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;

            do_notify_parent(tsk, signal);

           } else if (tsk->ptrace) {

             do_notify_parent(tsk, SIGCHLD); 

           }

中看出来。

6、 为什么子进程退出时,如果父进程没有调用wait等待子进程结束,则子进程会变成僵尸进程?

分析如下:在内核源码中有如下的代码:

                do_exit->exit_notify->

                           state = EXIT_ZOMBIE

                       if (tsk->exit_signal == -1 &&

                            (likely(tsk->ptrace == 0) ||

                            unlikely(tsk->parent->signal->flags & SIGNAL_GROUP_EXIT)))

                         state = EXIT_DEAD;

                       tsk->exit_state = state;

说明如果定义了子进程退出时向父进程发送信号,则设置进程状态为EXIT_ZOMBIE,否则为EXIT_DEAD。而子进程退出时一定会向父进程发送

信号,所以进程的状态为EXIT_ZOMBIE,如果此时父进程调用wait等待子进程结束的话,由do_wait->wait_task_zombie函数可以将进程的状态设置为EXIT_DEAD,并且释放进程的内核堆栈资源,最后由put_task_struct将其task_struct结构体释放掉。否则子进程会变成僵尸进程。 


相关内容