进程的描述和进程的创建
注:作者:臧文君,原创作品转载请注明出处,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一、进程的描述
操作系统的三大功能:进程管理,内存管理和文件系统。
1、进程描述符task_struct数据结构
(1)进程控制块PCB---task_struct
(2)进程描述符提供了内核所需了解的进程信息。
task_struct中包含:进程状态,进程打开的文件,进程优先级信息。
tty_struct控制台
fs_struct文件系统
files_struct打开的文件描述符
mm_struct内存管理
signal_struct通讯信号的描述
(3)struct task_struct的数据结构很庞大
(4)Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如:就绪状态和运行状态都是TASK_RUNNING。
进程调用do_exit()终止执行,TASK_ZOMBIE(进程被终止)。
阻塞态:TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE(等待中)。
2、进程的标识pid
3、task_struct数据结构分析
(1)运行状态state:-1 unrunnable,0 runnable,>0 stopped。
进程的内核堆栈stack。
ifdef CONFIG SMP条件编译,多处理器时用到。
进程链表list_head tasks
4G的进程地址空间
(2)进程的父子关系
P0有三个儿子P1,P2,P3;P1有两个兄弟P2,P3;P3有一个儿子P4。
(3)CPU相关的状态:thread_struct
文件系统:fs_struct
二、进程的创建
1、进程的创建概览及fork一个进程的用户态代码
fork()是用户态用于创建一个子进程的系统调用。
fork()系统调用在父进程和子进程各返回一次,在子进程中,pid的返回值为0,执行else的代码,在父进程中,pid的返回值为子进程的ID,执行else if的代码。
2、系统调用再回顾
3、一个新创建的子进程从哪里开始执行?
创建新进程是通过复制当前进程来实现的。
系统调用内核处理函数sys_fork、sys_clone、sys_vfork
4、do fork中的copy process就是创建一个进程的主要代码。
dup task struct复制整个PCB。
拷贝内核堆栈数据和指定新进程的第一条指令地址:*childregs = *current_pt_regs();
5、创建的新进程是从哪里开始执行的?
p->thread.ip = (unsigned long) ret_from_fork;
当子进程获得CPU控制权时,它的return from fork可以把后面堆栈中的iret返回到用户态。
三、使用gdb跟踪创建新进程的过程
1、先删除menu,再克隆一个新的menu。
2、用test_fork.c覆盖test.c:
cd menu
mv test_fork.c test.c
make rootfs//编译
3、调试:
qemu-system-x86_64 -kernel bzImage -initrd /home/YL/menu/rootfs.img -s -S
gdb /usr/src/linux-source-4.4/vmlinux
set arch i386:x86-64
target remote:1234
4、设置断点:b sys_clone
b do_fork
b dup_task_struct
b copy_process
b copy_thread
b ret_from_fork
5、按c继续执行,但会报错显示超时,此时重启一遍gdb即可。
6、按n单步执行,按s进入某个函数中去单步执行。
当前进程的内核堆栈中压的寄存器复制到进程中:
设置子进程被调度的IP,是子进程的起点ret_from_fork:
总结:
本次课程学习的是进程的结构分析以及进程的创建。Linux系统创建一个新进程主要依靠fork(),fork()是用户态用于创建一个子进程的系统调用,再由fork()调用do_fork来实现进程的创建。
先复制一个PCB——task_struct,再给新进程分配一个新的内核堆栈,并且修改复制过来的进程数据,比如pid、进程链表等等。通过以上步骤,就可以创建一个新进程。
新进程的起点是ret_from_fork,通过*childregs = *current_pt_regs()语句复制内核堆栈。
在实验的过程中,会遇到gdb的问题,显示连接超时,此时需要重启gdb即可。另外,调试内核时还需要注意路径的问题。若没有给出路径,则是在当前目录下查找文件。