天天看点

进程、线程、fork、COW、父子进程、内核线程、优先级调度

进程、线程、fork、COW、父子进程、内核线程、优先级调度

系统中所有进程的 task_struct 数据结构都通过 list_head 类型的双向链表链在一起,Linux 内核提供了一个很常用的宏for_each_process§,用来扫描系统中的所有的进程。这个宏从init_task进程开始遍历,一直循环到init_task为止。另外,宏for_each_porcess_thread()来遍历系统中所有的线程。

Linux 内核提供了current宏,用它可以很方便地找到当前正在运行的进程的task_struct数据结构,current宏的实现和具体的系统架构相关,使用专门的寄存器存放当前进程的 task_struct 数据结构。

fork() 通过写时复制技术复制当前进程的相关信息来创建一个全新的子进程。这时子进程和父进程运行在各自的进程地址空间中,但是共享相同的内容。另外,它们有各自的 PID。execve()函数负责读取可执行文件,将其装入子进程的地址空间并开始运行,这时父进程和子进程才开始分道扬镳。

写时复制技术就是父进程在创建子进程时不需要复制进程地址空间的内容给子进程,只需要复制父进程的进程地址空间的页表给子进程,这样父子进程就可以共享相同的物理内存。当父子进程中有一方需要修改某个物理页面的内容时,触发写保护的缺页异常,然后才把共享页面的内容复制出来,从而让父子进程拥有各自的副本。写时复制技术可以推迟甚至避免复制数据。

fork()函数。fork 原语是最基本的进程创建函数。使用 fork() 函数来创建子进程,子进程和父进程将拥有各自独立的进程地址空间,但是共享物理内存资源,包括进程上下文、进程栈、内存信息、打开的文件描述符、进程优先级、根目录、资源限制、控制终端等。在创建期间,子进程和父进程共享物理内存空间,当它们开始运行各自的程序时,他们的进程地址空间开始分道扬镳,这得益于写时复制技术的优势。

子进程和父进程有如下一些区别:

  • 子进程和父进程的 ID 不一样。
  • 子进程不会继承父进程的内存方面的锁,比如mlock()
  • 子进程不会继承父进程的一些定时器,比如 setitimer()、alarm()、timer_create()
  • 子进程不会继承父进程的信号量,比如 semop()

clone()函数通常用来创建用户线程。Linux 内核中没有专门的线程,而是把线程当作普通进程对待,在内核中仍然以task_struct数据结构来描述,而没有用特殊的数据结构或者调度算法来描述线程。

内核线程其实就是运行在内核地址空间中的进程,它和普通用户进程的区别在于内核线程没有独立的进程地址空间,也就是 task_struct 数据结构中的 mm 指针被设置为 NULL,因而只能运行在内核地址空间中,和普通进程一样参与系统调度。所有的内核线程都共享内核地址空间。

继续阅读