
本文主要是“”的摘要,主要指示代码中父子进程与父子线程之间的对应关系.
fork函数由pthread_create,fork,vfork,kernel_thread和其他函数复用,因此在分析时,请注意每个函数的特性. fork所做的通常是将当前结构复制(直接分配)到当前进程p,然后将当前工作目录,信号,信号量,信号处理函数和名称空间复制到当前进程p(因为这样的复制实际上是分叉使用COW机制非常耗时,在复制了刚才提到的几个重要结构后,复制其non-task_struct管理结构使用了复制其映射表的操作,并且相应的页表将设置为只读,因此可以减少复制时间),最后设置新进程的寄存器并返回函数,返回值,并通过copy_thread_tls返回到应用程序空间(因为新进程被直接添加到正在运行的CPU队列中,所以在fork返回之后,是否要首先运行父进程还是子进程取决于当时的运行情况.
注意: vfork确保子进程先运行,而父进程只能安排在调用exec或退出后运行. 也就是说,当子进程调用exec或exit时,将触发ake_up($ vfork)信号.
所谓的COW机制如上所述: 它仅复制数据的页表,然后将页表设置为只读. 因此,当进程读取数据时,父进程和子进程的内存空间仍然共享,但是如果进程生成写操作,则由于没有写权限,它将生成do_page_fault错误. 重新定义do_page_falut函数是因为它分配了新的内存并建立了新的映射关系,稍后将在讨论虚拟地址时对此进行解释.

注意: 内核线程只有一个堆栈,即内核堆栈;一个应用程序进程/线程有两个堆栈,分别是内核堆栈和用户堆栈. 毫无疑问,用户堆栈主要用于用户模式(armv7的usr模式,armv8的EL0例外),内核堆栈用于内核模式(armv7的svc模式,armv8的EL1例外). 至于用户堆栈和内核堆栈,将在以后的体系结构中进行解释.
1. 子进程与父进程之间的关系(子进程可以通过task_struct-> real_parent获得父进程的task_struct. 注意: 当涉及到调试工具时,task_struct-> parent指向调试进程,否则为父进程和real_parent全部指向父进程),如下所示:

父进程可以通过task_struct->子进程列表找到所有子进程. 子进程可以通过task_struct-> real_parent找到其父进程.

2. 进程与空闲进程之间的关系,如下所示:

整个系统的所有进程都可以在空闲进程的任务列表中找到.
3. 线程与线程组之间的关系,如下所示:


线程组的领导者实际上是一个进程,它的所有成员都由它使用pthread_create创建. 它的所有组成员都可以通过task_struct-> group_leader找到它,同样fork 函数,它可以通过task_struct-> thread_group遍历它的所有组成员. 程组中,所有线程的父进程与其组领导线程的父进程相同,即task_struct-> real_parent全部指向同一进程.
4. 程共享信号中,线程的位置如下:


因为在Linux中,同一线程组下的所有线程实际上共享信号和信号处理功能. 这意味着什么?在Linux线程中,除了SIGSEGV(由于其自身的异常操作),SIGBUS,SIGFPE,SIGILL和其他信号之外,每个线程都可以接收该信号(因为该线程共享该信号),但是只要其中一个线程处理此信号(可以理解为: 当信号到达时,程序会通过task_struct-> signal-> thread_node列表查找可以处理该信号的线程,如果找到,它将中断;如果找不到, ,它将查询下一个线程),其他线程将不再接收此信号. 因此,如果从pthread_create创建的线程不想处理该信号,那么我们需要做一些特殊的处理来屏蔽信号(每个线程都有自己的挂起信号掩码和阻塞信号掩码).
如上所示,每个线程的信号指向相同的task_struct->信号,可以通过遍历task_struct-> signal-> thread_head来获取当前线程组下的所有线程fork 函数,从而获得该信号. 这些线程通过task_struct-> thread_node附加到signal-> thread_head.
注意: 无论是进程还是线程,都有一个不同的pid,并且同一线程组下的所有线程都只有一个tgid. 此tgid的值是线程组头的pid值.
5. pid空间中进程的结构,如下所示:

如上所示,可以看出fork函数中的进程首先通过alloc_pid函数从pid缓存中申请了struct pid结构(该结构会将pid添加到alloc_pid中的pid_hash []表中) ,然后添加pid. 将结构分配给task_struct-> pids.pid,最后通过hlist_add_head_rcu函数将task_struct添加到pid指向的任务列表中(此列表分为三类: PID,PGID,SID ),以后可以通过pid找到相应的pid. task_struct也可以通过task_struct找到相应的pid链.
注意: 初始化Linux内核时,将初始化pid哈希. pidhash_init()和pidmap_init()存在于内核/ pid.c文件中. 常见的搜索功能: get_pid_task,find_get_pid,get_task_pid,find_task_by_vpid,find_task_by_pid_ns等.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-201681-1.html
马云这话不对