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中压入栈的参数。








 

 

 

 

 

 

转载于:https://www.cnblogs.com/20135201lcx2/p/5348322.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值