20135201李辰希《Linux内核分析》第六周 进程的描述与创建

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20135201李辰希《Linux内核分析》第六周 进程的描述与创建相关的知识,希望对你有一定的参考价值。

李辰希 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一.进程的描述

操作系统的三大管理功能:

  • 进程管理(最重要的)
  • 内存管理
  • 文件系统

为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。

进程控制块PCB task_struct:

  • 进程状态
  • 进程打开的文件
  • 进程优先级信息

task_struct总体数据结构的抽象:

tty:控制台
fs:文件系统
files:文件描述符
mm:内存管理
signal:信号描述

进程状态:不同于操作系统(就绪、运行、阻塞),如Linux中就绪状态和运行状态都是TASK_RUNNING

                TASK_RUNNING具体是就绪还是执行,要看系统当前的资源分配情况;

       TASK_ZOMBIE也叫僵尸进程

 

二.进程的创建

——进程起源回顾:

道生一(start_kernel....cpu_idle)
一生二(kernel_init和kthreadd)
二生三(即前面0、1和2三个进程)
三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)

0号进程,是代码写死的,1号进程复制0号进程PCB,再修改,再加载可执行程序。

1.fork代码

1.#include <stdio.h>
2.#include <stdlib.h>
3.#include <unistd.h>
4.int main(int argc, char * argv[])
5.{
6.int pid;
7./* fork another process */
8.pid = fork();
9.if (pid < 0) 
10.{ 
11./* error occurred */
12.fprintf(stderr,"Fork Failed!");
13.exit(-1);
14.} 
15.else if (pid == 0) //pid == 0和下面的else都会被执行到(一个是在父进程中即pid ==0的情况,一个是在子进程中,即pid不等于0)
16.{
17./* child process */
18.printf("This is Child Process!\n");
19.} 
20.else 
21.{  
22./* parent process  */
23.printf("This is Parent Process!\n");
24./* parent will wait for the child to complete*/
25.wait(NULL);
26.printf("Child Complete!\n");
27.}
28.}


2.创建一个新进程在内核中的执行过程

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;

Linux通过复制父进程来创建一个新进程:

do_fork主要是复制了父进程的task_struct,然后修改必要的信息,从而得到子进程的task_struct。

理解这一个过程的一个想象的框架:

复制一个PCB——task_struct

err = arch_dup_task_struct(tsk, orig);

要给新进程分配一个新的内核堆栈

ti = alloc_thread_info_node(tsk, node);
tsk->stack = ti;
setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈



3.一个新创建的子进程,(当它获得CPU之后)是从哪一行代码进程执行的?
  • 与之前写过的my_kernel相比较,kernel中是可以指定新进程开始的位置(也就是通过eip寄存器指定代码行)。fork中也有相似的机制
  • 这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process

    1.*childregs = *current_pt_regs(); //复制内核堆栈,并不是全部,只是regs结构体(内核堆栈栈底的程序)
    2.childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
    3. 
    4.p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
    5.p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址,也就是说返回的就是子进程的空间了

 

三.实验:分析Linux内核创建一个新进程的过程

1.启动MenuOS

cd LinuxKernel   
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_fork.c test.c
make rootfs
  • 技术分享
 

技术分享

2.gdb调试fork命令

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

gdb
file linux-3.18.6/vmlinux
target remote:1234

技术分享

3.设置断点:

b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork

技术分享

4.继续执行之后,停在了do_fork的位置。

技术分享


四.总结

对“Linux系统创建一个新进程”的理解:
p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶 
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

将子进程的ip设置为ret_ form _ fork的首地址,因此子进程是从ret_ from_ fork开始执行的。
将父进程的regs参数赋值到子进程的内核堆栈,*childregs的类型为pt_regs,其中存放了SAVE ALL中压入栈的参数。








 

 

 

 

 

 

以上是关于20135201李辰希《Linux内核分析》第六周 进程的描述与创建的主要内容,如果未能解决你的问题,请参考以下文章

20135201李辰希 《Linux内核分析》第四周 扒开系统调用的“三层皮”

20135201李辰希 《Linux内核分析》第五周 扒开系统调用的“三层皮”(下)

20135201李辰希《Linux内核分析》第一周 计算机是如何工作的?

《Linux内核分析》第六周学习笔记

《Linux内核与分析》第六周

linux内核分析第六周-分析Linux内核创建一个新进程的过程