第六章 进程的描述和进程的创建
- 操作系统内核实现操作系统的三大管理功能,对应了3个抽象概念:
- **进程管理——进程 **
- 内存管理——虚拟内存
- 文件系统——文件
- 使用PCB描述进程。描述进程的机构,在Linux中使用数据结构struct task_struct描述进程,包含state进程状态,stack堆栈,fs文件系统等的描述。
+
struct list_head tasks;//所有的进程用双向链表链起来
+
struct mm_struct *mm,*active_mm;//mm和active_mm是和进程地址空间、内存管理相关的数据结构指针
+ 进程之间的父子、兄弟关系
struct list_head __rcu *real_parent;//当前进程的父进程
struct list_head __rcu *parent;
struct list_head children;//当前进程的子进程
struct list_head sibling;//当前进程的兄弟进程
struct list_head *group_leader;//每一个进程是一些进程组的成员之一,进程组组都有一个进程组长(group leader)。进程组的所有 IO 输入输出都会引导到进程组长进程那里去。当一个进程被创建时,父进程的进程组长也看作是子进程的进程组长。系统初始化的时候,init 进程既是它自己的进程组长,同时也是其他所有进程的进程组长。
+ 保存进程上下文中CPU相关的一些状态信息的数据结构
struct thread_struct thread;//sp用来保存进程上下文中ESP寄存器的状态,ip用来保存进程上下文中EIP寄存器的状态
- 在Linux系统中,TASK_RUNNING包括两种状态:进程就绪和进程运行。这两种状态的区分取决于进程是否获得CPU的分配权。
- 阻塞态: TASK_INTERRUPTIBLE 可以被信号或wake_up()唤醒。TASK_UNINTERRUPTIBLE:只能被wake_up()唤醒。
- 0号进程的初始化
set_task_stack_end_magic(&init_task);
内核的第一个进程0号进程iinit_task的进程描述符结构体变量的初始化是通过硬编码方式确定下来的,所有其他的进程都是通过do_fork的方式复制父进程来初始化的。
- 进程的创建
- 用户态创建进程的方法-fork系统调用
-
fork,vfork,clone 都是通过do_fork()函数创建进程
-
fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。
-
fork()函数最大的特点就是被调用一次,返回两次
-
父子进程共享内存存储空间
-
- do_fork()函数
- clone_flags:子进程创建的相关标志,通过此标志对父进程资源进行有选择的复制。
- **stack_start **:子进程用户态堆栈地址
- reg:指向pt_regs结构体的指针。(pt_regs结构体中保存的是int指针和SAVE_ALL等压入栈的CPU寄存器的值)
- **stack_size **:用户栈大小。通常为0.
- *parent_tidptr: 指向父进程pid的指针
- ***child_tidptr **:指向子进程pid的指针
- 用户态创建进程的方法-fork系统调用
实验部分-跟踪分析进程创建过程
- 删除原有的menu,克隆新的menu
- 编译运行出来可以看到fork命令
- 按照5.2中的方法,首先启动内核
- 在sys_clone,do_fork_,do_task_struct,copy_process,copy_thread,ret_from_fork等处设置断点并继续执行。