我爱编程

Linux进程创建

2018-04-11  本文已影响0人  小忍甜甜圈

进程描述符

进程描述符(process descriptor)包含了一个具体进程的所有信息。他的结构体定义在<linux/sched.h>中,代码见linux/sched.h,类型为task_struct。进城描述符几乎包含了所有进程相关的上下文,下面简单列举了一些:

进程描述符的存放

在kernel<2.6中,进程的task_struct存放在各自的内核栈的尾部,不过在后来的内核中,由于使用了slab分配器来动态生成task_struct,所以还需要在栈尾部的thread_info中存放task_struct的指针。

在寄存器富裕的硬件体系架构,可能会有一个专门的寄存器用来存放当前进程task_struct的指针,因为在内核的进程处理相关逻辑中,这个结构体经常被用到。不过在x86这种寄存器不富裕的架构,当通过current宏返回进程描述符时,就需要通过内核栈尾部的thread_info寻找task_struct的位置了。

state 进程状态

当内核需要调整某个进程的状态时,可以使用set_task_state(task, state),可以调整到如下五种状态:

进程创建

当调用fork创建子进程时,Linux会通过拷贝当前进程来创建子进程。由于使用了写时拷贝页技术(CopyOnWrite),所以进程的创建只需要复制页表和创建进程描述符,速度是比较快的。

进程拷贝调用链为fork -> clone -> do_fork(kernel/fork.c) -> copy_process,其中copy_process

  1. 首先创建一个内核栈,并拷贝父进程的thread_info以及进程描述符到内核栈中
  2. 将进程描述符中非继承的统计量设初始值,并将状态置为TASK_UNINTERRUPTIABLE,保证进程不会运行
  3. 分配一个有效的PID
  4. 拷贝或共享打开的文件句柄,文件系统信息,信号处理函数,进城地址空间和命名空间等。
  5. 扫尾工作并返回子进程指针
线程仅仅被视为与其他进程共享某些资源的进程

进程终结

当进程显式调用exit()或者main()函数返回隐式调用exit,或者接收到无法处理/忽略的信号量或异常,就得调用do_exit来终结这个进程:

  1. 状态转换,将task_struct中的flag设置为PF_EXITING
  2. 清理/释放IPC信号、地址空间、定时器、文件引用计数等系统资源
  3. 在进程描述符中设置exitcode,并设置exit_state为EXIT_ZOMBIE
  4. 最后调用schedule()切换到执行新的进程,该进程结束
    不过还剩下了task_struct以及内核栈中的数据没有被释放,他们的作用就是通知父进程,子进程的结束状态。父进程将会收到一个信号量,此时可以通过wait来获取exitcode以及删除子进程的参与信息(内核栈,进程描述符,线程信息)

孤儿进程

当一个父进程退出时,就需要为他的子进程重新设置父进程,来接收未来的exitcode。首先会尝试在当前进程组中寻找一个进程作为父亲,如果找不到,就让init进程作为父进程。

上一篇 下一篇

猜你喜欢

热点阅读