操做系统三大功能:数据结构
进程描述符task_struct数据结构atom
task _ struct:为了管理进程,内核必须对每一个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。spa
进程的状态:Linux进程的状态(就绪态、运行态、阻塞态)操作系统
进程的标示pid:用来标示进程线程
进程描述符task_struct数据结构(重要部分):3d
1 struct task_struct { 2 volatile long state; /* 进程的运行状态-1 unrunnable, 0 runnable, >0 stopped */ 3 void *stack; /*指定了进程的内核堆栈*/ 4 atomic_t usage; 5 unsigned int flags; /* 每个进程的标识符 */ 6 7 int on_rq; /*运行队列*/ 8 9 pid_t pid; /*进程标识符*/ 10 11 struck list_head task; /*进程链表*/ 12 13 /*父子进程*/ 14 struct task_struct __rcu *real_parent; /* real parent process */ 15 struct task_struct __rcu *parent; 16 17 struct list_head children; /* list of my children */
start _ kernel代码中的rest _ init建立两个内核线程,kernel _ init和kthreadd。调试
kernel _ init将用户态进程init启动,是全部用户态进程的祖先。rest
kthreadd是全部内核线程的祖先。code
0号进程是全部线程的祖先。(0号进程时手工写的)orm
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) 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 }
这里特别说明一下:else if和if两个模块的代码都会被执行,子进程和父进程都会返回,子进程返回0,父进程返回子进程pid。
1. 删除menu,克隆新的menu。
2.查看help
3.gdb调试
4.设置断点
5.单步追踪
总结:
新的进程从哪里开始?
1 *childregs = *current_pt_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经过复制父进程来建立一个新进程:复制父进程PCB--task_struct来建立一个新进程,要给新进程分配一个新的内核堆栈。
修改复制过来的进程数据,好比pid、进程链表等等执行copy_process和copy_thread。
设置sp调度到子进程时的内核栈顶,ip转到子进程时的第一条指令地址
以后,当子进程得到CPU的控制权开始运行的时候,ret _ form _ fork能够将后面的堆栈出栈,从iret返回到用户态,从而切换到子进程的用户空间,完成新进程的建立。