Linux内核分析 笔记六 进程的描述和进程的创建 ——by王玥
2016-03-29 14:49
567 查看
一、知识点总结
(一)进程的描述
1.操作系统内核里有三大功能:进程管理
内存管理
文件系统
2.进程描述符:task_struct
2.进程描述符——structtask_struct
1.pid_tpid又叫进程标识符,唯一地标识进程 2.双向循环链表链接起了所有的进程,也表示了父子、兄弟等进程关系 3.structmm_struct指的是进程地址空间,涉及到内存管理(对于X86而言,一共有4G的地址空间) 4.thread_structthread与CPU相关的状态结构体 5.struct*file表示打开的文件链表 6.Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈
3.进程状态转换图
数据结构分析:structtask_struct{ 1236 volatilelong state; //运行状态 1237 void* stack;//进程的内核堆栈 1238 atomic_t usage; 1239 unsignedint flags;//每个进程的标识符 1240 unsignedint
#ifdefCONFIG_SMP//这部分是条件编译 1243 struct llist_node wake_entry; 1244 int on_cpu; 1245 struct task_struct* last_wakee; 1246 unsignedlong wakee_flips; 1247 unsignedlong wakee_flip_decay_ts; .....
structlist_head tasks;//这部分很关键,是进程的列表 1296#ifdef CONFIG_SMP 1297 struct plist_node pushable_tasks; 1298 struct rb_node pushable_dl_tasks; 1299#endif 1300 1301 struct mm_struct* mm,* active_mm;//和地址的内存空间,内存管理有关的,每个地址有独立的地址空间。 1302#ifdef CONFIG_COMPAT_BRK 1303 unsigned brk_randomized:1; 1304#endif 1305 /*per-threadvmacaching*/ 1306 u32 vmacache_seqnum; 1307 struct vm_area_struct* vmacache[ VMACACHE_SIZE]; 1308#ifdefined( SPLIT_RSS_COUNTING) 1309 struct task_rss_stat rss_stat; 1310#endif 链表的数据结构如下: 它是一个双向链表,参见include/linux/list.h
1330 pid_t pid;//进程的pid,来标识某一个进程 1331 pid_t tgid; ....
/*1338 *pointersto(original)parentprocess,youngestchild,youngersibling,//进程的父子关系 1339 *oldersibling,respectively.(p->fathercanbereplacedwith 1340 *p->real_parent->pid) 1341 */ 1342 struct task_struct __rcu* real_parent;/*realparentprocess*/ 1343 struct task_struct __rcu* parent;/*recipientofSIGCHLD,wait4()reports*/ 1344 /* 1345 * children/ siblingformsthelistofmynaturalchildren 1346 */ 1347 struct list_head children; /*listofmychildren*/ 1348 struct list_head sibling; /*linkageinmyparent'schildrenlist*/ 1349 struct task_struct* group_leader; /*threadgroupleader*/ 1350
... 1411/*CPU-specificstateofthistask*/
1412 struct thread_struct thread;//当前CPU相关的状态,在进程切换时起关键作用。
(二)进程的创建
1.进程的状态以及fork一个进程的用户态代码2.fork系统调用在父进程和子进程各返回一次
3.TASK_RUNNING具体是就绪还是执行,要看系统当前的资源分配情况
4.TASK_ZOMBIE也叫僵尸进程
fork一个子进程的代码
fork代码
1.#include<stdio.h> 2.#include<stdlib.h> 3.#include<unistd.h> 4.intmain(intargc,char*argv[]) 5.{ 6.intpid; 7./*forkanotherprocess*/ 8.pid=fork(); 9.if(pid<0) 10.{ 11./*erroroccurred*/ 12.fprintf(stderr,"ForkFailed!"); 13.exit(-1); 14.} 15.elseif(pid==0)//pid==0和下面的else都会被执行到(一个是在父进程中即pid==0的情况,一个是在子进程中,即pid不等于0) 16.{ 17./*childprocess*/ 18.printf("ThisisChildProcess!\n"); 19.} 20.else 21.{ 22./*parentprocess*/ 23.printf("ThisisParentProcess!\n"); 24./*parentwillwaitforthechildtocomplete*/ 25.wait(NULL); 26.printf("ChildComplete!\n"); 27.} 28.}
iret与int0x80指令对应,一个是弹出寄存器值,一个是压入寄存器的值
如果将系统调用类比于fork();那么就相当于系统调用创建了一个子进程,然后子进程返回之后将在内核态运行,而返回到父进程后仍然在用户态运行
回顾:系统调用的进程创建过程
创建一个新进程在内核中的执行过程
1.一个新创建的子进程,(当它获得CPU之后)是从哪一行代码进程执行的?
与之前写过的my_kernel相比较,kernel中是可以指定新进程开始的位置(也就是通过eip寄存器指定代码行)。fork中也有相似的机制
这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_threadincopy_process
fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
复制一个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,而非复制内核堆栈
要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。
从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_threadincopy_process
*childregs=*current_pt_regs();//复制内核堆栈
childregs->ax=0;//为什么子进程的fork返回0,这里就是原因!
p->thread.sp=(unsignedlong)childregs;//调度到子进程时的内核栈顶
p->thread.ip=(unsignedlong)ret_from_fork;//调度到子进程时的第一条指令地址
dup_thread复制父进程的PCB
copy_process修改复制的PCB以适应子进程的特点,也就是子进程的初始化
分配一个新的内核堆栈(用于存放子进程数据)
内核堆栈的一部分也要从父进程中拷贝
-
根据拷贝的内核堆栈情况设置eip,esp寄存器的值
使用gdb跟踪创建新进程的过程
更新menu内核,然后删除test_fork.c以及test.c(以减少对之后实验的影响)编译内核,可以看到fork命令
启动gdb调试,并对主要的函数设置断点
在MenuOS中执行fork,就会发现fork函数停在了父进程中
继续执行之后,停在了do_fork的位置。然后n单步执行,依次进入copy_process、dup_task_struct。按s进入该函数,可以看到dst=src(也就是复制父进程的struct)
3.在copy_thread中,可以看到把task_pg_regs(p)也就是内核堆栈特定的地址找到并初始化
4.到了159、160行的代码就是把压入的代码再放到子进程中:
*children=*current_pt_regs();
childregs->ax=0;
164行,是确定返回地址
p->thread.ip=(unsignedlong)ret_from_fork;
最后,可以输入finish使得进程运行完。[/code]
相关文章推荐
- 《Linux内核设计与实现》读书笔记三
- 第六周——分析Linux内核创建一个新进程的过程
- linux下的hosts.allow和hosts.deny
- Could not retrieve mirrorlist
- linux之vim命令
- Centos6.5 SVN服务器 搭建及配置
- CentOS6.5下安装rzsz
- 使用7z在Linux系统压缩备份文件
- Linux内核分析作业 NO.6
- Linux内核分析第十八章读书笔记
- Linux获取帮助
- Linux 系统查看常用命令
- CentOS系统启动流程
- Linux中的sed详解
- 20135337朱荟潼 Linux第六周学习总结——进程的描述和进程的创建
- 嵌入式Linux之我行——S3C2440上MMC/SD卡驱动实例开发讲解(一)
- CentOS 6.3下配置LVM(逻辑卷管理)
- CentOS7下Firewall防火墙配置用法详解
- RedHat下yum命令安装
- RedHat下rz/sz命令安装及使用方法