深入Linux内核架构-进程管理和调度(五)

在此讨论各个copy_xxx函数的概述其作用。

kernel/fork.c

如果CLONE_SYSVSEM置位,则copy_semundo使用父进程的System V信号量。

如果CLONE_FILES置位,则copy_files使用父进程的文件描述符;否则创建新的files结构,其中包含的信息与父进程相同。该信息的修改可以独立于原结构。

如果CLONE_FS置位,则copy_fs使用父进程的文件系统上下文(task_struct->fs)。这是一个fs_struct类型的结构,包含了诸如根目录、进程的当前工作目录之类的信息。

如果CLONE_SIGHAND或CLONE_THREAD置位,则copy_sighand使用父进程的信号处理程序。

如果CLONE_THREAD置位,则copy_signal与父进程共同使用信号处理中不特定于处理程序的部分。

如果COPY_MM置位,则copy_mm让父进程和子进程共享同一地址空间。在这种情况下,两个进程使用同一个mm_struct实例,task_struct->mm指针即指向该实例。

如果COPY_MM没有置位,并不意味着需要复制父进程的整个地址空间。内核确实会创建页表的一份副本,但并不复制页的实际内容。这是使用Copy-On-Write(写时复制)机制完成的,仅当其中一个进程将数据写入页时,才会进行实际复制。

copy_namespaces有特别的调用语义。它用于建立子进程的命名空间。如果没有指定CLONE_NEWxyz,则与父进程共享相应的命名空间,否则创建一个新的命名空间。copy_namespaces相当于调度程序,对每个可能的命名空间,分别执行对应的复制例程。因为本质上就是复制数据或通过引用计数的管理来共享现存的实例。

copy_thread是一个特定于体系结构的函数,用于复制进程中特定于线程的数据。备注:这里的特定于线程并不是指某个CLONE标志,也不是指操作对线程而非整个进程执行。其语义是指复制执行上下文中特定于体系结构的所有数据。

重要的是填充task_struct->thread的各个成员。这是一个thread_struct类型的结构,其定义是体系结构相关的。它包含了所有寄存器(和其他信息),内核在进程之间切换时需要保存和恢复进程的内容,该结构可用于此。

为理解各个thread_struct结构的布局,需要深入了解各种CPU的相关知识。

回到对copy_process的讨论,内核必须填好task_struct中对父子进程不同的各个成员。包含下列一些:

task_struct中包含的各个链表元素,例如sibling和children;

间隔定时器成员cpu_timers;

待决信号列表( pending);

在用之前描述的机制为进程分配一个新的pid实例之后,则保存在task_struct中。对于线程,线程组ID与分支进程(即调用fork/clone的进程)相同:

kernel/fork.c
 

pid_nr函数对给定的pid实例计算全局数值PID。

对普通进程,父进程是分支进程。对于线程来说有些不同:由于线程被视为分支进程内部的第二(或第三、第四,等等)个执行序列,其父进程应是分支进程的父进程。

kernel/fork.c
 

非线程的普通进程可通过设置CLONE_PARENT触发同样的行为。对线程来说还需要另一个校正,即普通进程的线程组组长是进程本身。对线程来说,其组长是当前进程的组长:

kernel/fork.c

新进程接下来必须通过children链表与父进程连接起来。这是通过辅助宏add_parent处理的。

kernel/fork.c

thread_group_leader只检查新进程的pid和tgid是否相同。倘若如此,则该进程是线程组的组长。在这种情况下,还需要完成更多必要的工作。

回想一下,在非全局命名空间的进程命名空间中,各个进程有特定于该命名空间的init进程。如果通过置位CLONE_NEWPID创建一个新的PID命名空间,那么init进程的角色必须由调用clone的进程承担。

新进程必须被加到当前进程组和会话。

最后, PID本身被加到ID数据结构的体系中。创建新进程的工作就此完成!

5、创建线程时的特别问题

用户空间线程库使用clone系统调用来生成新线程。该调用支持(上文讨论之外的)标志,对copy_process(及其调用的函数)具有某些特殊影响。但有一点应该记住,在Linux内核中,线程和一般进程经常用作同义词(如前所述, 线程也经常用于指进程的体系结构相关部分)。在本节中,重点讲解用户线程库用于实现多线程功能的标志。

CLONE_PARENT_SETTID将生成线程的PID复制到clone调用指定的用户空间中的某个地址( parent_tidptr,传递到clone的指针) :

kernel/fork.c

复制操作在do_fork中执行,此时新线程的task_struct尚未初始化,copy操作尚未创建新线程的数据。

CLONE_CHILD_SETTID首先会将另一个传递到clone的用户空间指针(child_tidptr) 保存在新进程的task_struct中。

kernel/fork.c

p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;

在新进程第一次执行时,内核会调用schedule_tail函数将当前PID复制到该地址。

kernel/sched.c

CLONE_CHILD_CLEARTID首先会在copy_process中将用户空间指针child_tidptr保存在task_struct中,这次是另一个不同的成员。

kernel/fork.c

p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;

在进程终止时, 将0写入clear_child_tid指定的地址。

kernel/fork.c

sys_futex,一个快速的用户空间互斥量,用于唤醒等待线程结束事件的进程。

上述标志可用于从用户空间检测内核中线程的产生和销毁。CLONE_CHILD_SETTID和CLONE_PARENT_SETTID用于检测线程的生成。CLONE_CHILD_CLEARTID用于在线程结束时从内核向用户空间传递信息。在多处理器系统上这些检测可以真正地并行执行。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值