Linux内核作业--分析Linux内核创建一个新进程的过程
2015-04-12 23:57
501 查看
说明
刘玉龙
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
准备工作
task_struct的应该会存在哪些结构:
课堂提供在/linux-3.18.6/include/linux/sched.h中找到tast_struct的定义:
现在的Linux系统基本上是按照操作系统理论来进行设计的,但是在实现的过程中,理论往往是不够的,为了实现很多实际的需求,tast_struct还定义了很多额外的结构,来方便系统的相关管理,比如后面没有列出来的一些文件操作相关的结构,这些结构一般用于当一个进程没有按照规范来操作文件时,当进程被杀掉后,系统任然可以对这些不规范的操作进行管理。当然,后面还有很多内容也是如此,我们就不一一叙说了,我们只看创建一个进程的相关重点。
3.进程创建分析
fork函数到底如何进行对应的内核处理过程sys_clone。
Linux中,PCB task_struct中不包含哪个信息()?
进程状态
进程打开的文件
进程优先级信息
进程包含的线程列表信息
创建一个新进程在内核中的执行过程
fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
复制一个PCB——task_struct
err = arch_dup_task_struct(tsk, orig);
要给新进程分配一个新的内核堆栈
要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。
从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process
按流程来说首先会进入do_fork,在do_fork里,对一些情况进行判断。如果没有什么危险的情况,则开始进入copy_process。
copy_process函数在进程创建的do_fork函数中调用,主要完成进程数据结构,各种资源的初始化。初始化方式可以重新分配,也可以共享父进程资源,主要根据传入CLONE参数来确定。
dup_task_struct()
tsk = alloc_task_struct_node(node);为task_struct开辟内存
ti = alloc_thread_info_node(tsk, node);ti指向thread_info的首地址,同时也是系统为新进程分配的两个连续页面的首地址。
err = arch_dup_task_struct(tsk, orig);复制父进程的task_struct信息到新的task_struct里, (dst = src;)
tsk->stack = ti;task的对应栈
setup_thread_stack(tsk, orig);初始化thread info结构
set_task_stack_end_magic(tsk);栈结束的地址设置数据为栈结束标示(for overflow detection)
gdb跟踪sys_clone
用gdb来跟踪sys_clone,设置以下断点
运行后首先停在sys_clone处:
然后是do_fork,之后是copy_process:
接着进入copy_thread:
ret_from_fork按照之前的分析被调用,跟踪到syscall_exit后无法继续.如果想在本机调试system call,那么当你进入system call时,系统已经在挂起状态了。
总结
新进程是从哪里开始执行的——————–?
在之前的分析中,谈到copy_process中的copy_thread()函数,正是这个函数决定了子进程从系统调用中返回后的执行.
子进程执行ret_from_fork
=====================
在ret_from_fork之前,也就是在copy_thread()函数中*childregs = *current_pt_regs();该句将父进程的regs参数赋值到子进程的内核堆栈,
*childregs的类型为pt_regs,里面存放了SAVE ALL中压入栈的参数
故在之后的RESTORE ALL中能顺利执行下去.
7. 未完待续。。
8. 望手下留情
刘玉龙
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
准备工作
task_struct的应该会存在哪些结构:
1、进程状态、将纪录进程在等待、运行、或死锁 2、调度信息、由哪个调度函数调度、怎样调度等 3、进程的通讯状况 4、有插入进程链表的相关操作,因此必须有链表连接指针、当然是task_struct型 5、时间信息,比如计算好执行的时间、以便CPU资源的分配 6、标号,决定改进程归属 7、可以读写打开的一些文件信息 8、进程上下文和内核上下文 9、处理器上下文 10、内存信息等等
课堂提供在/linux-3.18.6/include/linux/sched.h中找到tast_struct的定义:
现在的Linux系统基本上是按照操作系统理论来进行设计的,但是在实现的过程中,理论往往是不够的,为了实现很多实际的需求,tast_struct还定义了很多额外的结构,来方便系统的相关管理,比如后面没有列出来的一些文件操作相关的结构,这些结构一般用于当一个进程没有按照规范来操作文件时,当进程被杀掉后,系统任然可以对这些不规范的操作进行管理。当然,后面还有很多内容也是如此,我们就不一一叙说了,我们只看创建一个进程的相关重点。
struct task_struct { volatile long state; //说明了该进程是否可以执行,还是可中断等信息 unsigned long flags; //进程号,在调用fork()时给出 int sigpending; //进程上是否有待处理的信号 mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同 //0-0xBFFFFFFF for user-thead //0-0xFFFFFFFF for kernel-thread //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度 volatile long need_resched; int lock_depth; //锁深度 long nice; //进程的基本时间片 //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER unsigned long policy; struct mm_struct *mm; //进程内存管理信息 int processor; //若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新 unsigned long cpus_runnable, cpus_allowed; struct list_head run_list; //指向运行队列的指针 unsigned long sleep_time; //进程的睡眠时间 //用于将系统中所有的进程连成一个双向循环链表, 其根是init_task struct task_struct *next_task, *prev_task; struct mm_struct *active_mm; struct list_head local_pages; //指向本地页面 unsigned int allocation_order, nr_local_pages; struct linux_binfmt *binfmt; //进程所运行的可执行文件的格式 int exit_code, exit_signal; int pdeath_signal; //父进程终止是向子进程发送的信号 unsigned long personality; int did_exec:1; pid_t pid; //进程标识符,用来代表一个进程 pid_t pgrp; //进程组标识,表示进程所属的进程组 pid_t tty_old_pgrp; //进程控制终端所在的组标识 pid_t session; //进程的会话标识 pid_t tgid; int leader; //表示进程是否为会话主管 struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr; struct list_head thread_group; //线程链表 struct task_struct *pidhash_next; //用于将进程链入HASH表 struct task_struct **pidhash_pprev; wait_queue_head_t wait_chldexit; //供wait4()使用 struct completion *vfork_done; //供vfork() 使用 unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值 …… };
3.进程创建分析
fork函数到底如何进行对应的内核处理过程sys_clone。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char * argv[]) { int pid; /* fork another process */ pid = fork(); if (pid < 0) { /* error occurred */ fprintf(stderr,"Fork Failed!"); exit(-1); } else if (pid == 0) { /* child process */ printf("This is Child Process!\n"); } else { /* parent process */ printf("This is Parent Process!\n"); /* parent will wait for the child to complete*/ wait(NULL); printf("Child Complete!\n"); } }
检测题目的最后一个是什么鬼 Linux中,fork()系统调用产生的子进程在系统调用处理过程中从(:ret_from_fork)处开始,我要留着以后看一下,莫名其妙
Linux中,PCB task_struct中不包含哪个信息()?
进程状态
进程打开的文件
进程优先级信息
进程包含的线程列表信息
创建一个新进程在内核中的执行过程
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_thread in copy_process
*childregs = *current_pt_regs(); //复制内核堆栈 childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因! p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶 p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
按流程来说首先会进入do_fork,在do_fork里,对一些情况进行判断。如果没有什么危险的情况,则开始进入copy_process。
copy_process函数在进程创建的do_fork函数中调用,主要完成进程数据结构,各种资源的初始化。初始化方式可以重新分配,也可以共享父进程资源,主要根据传入CLONE参数来确定。
dup_task_struct()
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; int node = tsk_fork_get_node(orig); int err; tsk = alloc_task_struct_node(node); if (!tsk) return NULL; ti = alloc_thread_info_node(tsk, node); if (!ti) goto free_tsk; err = arch_dup_task_struct(tsk, orig); if (err) goto free_ti; tsk->stack = ti; # ifdef CONFIG_SECCOMP tsk->seccomp.filter = NULL; # endif setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); set_task_stack_end_magic(tsk); # ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); # endif atomic_set(&tsk->usage, 2); # ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; # endif tsk->splice_pipe = NULL; tsk->task_frag.page = NULL; account_kernel_stack(ti, 1); return tsk; free_ti: free_thread_info(ti); free_tsk: free_task_struct(tsk); return NULL; }
tsk = alloc_task_struct_node(node);为task_struct开辟内存
ti = alloc_thread_info_node(tsk, node);ti指向thread_info的首地址,同时也是系统为新进程分配的两个连续页面的首地址。
err = arch_dup_task_struct(tsk, orig);复制父进程的task_struct信息到新的task_struct里, (dst = src;)
tsk->stack = ti;task的对应栈
setup_thread_stack(tsk, orig);初始化thread info结构
set_task_stack_end_magic(tsk);栈结束的地址设置数据为栈结束标示(for overflow detection)
gdb跟踪sys_clone
用gdb来跟踪sys_clone,设置以下断点
运行后首先停在sys_clone处:
然后是do_fork,之后是copy_process:
接着进入copy_thread:
ret_from_fork按照之前的分析被调用,跟踪到syscall_exit后无法继续.如果想在本机调试system call,那么当你进入system call时,系统已经在挂起状态了。
总结
新进程是从哪里开始执行的——————–?
在之前的分析中,谈到copy_process中的copy_thread()函数,正是这个函数决定了子进程从系统调用中返回后的执行.
int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg, struct task_struct *p) { ... *childregs = *current_pt_regs(); childregs->ax = 0; if (sp) childregs->sp = sp; p->thread.ip = (unsigned long) ret_from_fork; ... }
子进程执行ret_from_fork
ENTRY(ret_from_fork) CFI_STARTPROC pushl_cfi %eax call schedule_tail GET_THREAD_INFO(%ebp) popl_cfi %eax pushl_cfi $0x0202 # Reset kernel eflags popfl_cfi jmp syscall_exit CFI_ENDPROC END(ret_from_fork)
=====================
在ret_from_fork之前,也就是在copy_thread()函数中*childregs = *current_pt_regs();该句将父进程的regs参数赋值到子进程的内核堆栈,
*childregs的类型为pt_regs,里面存放了SAVE ALL中压入栈的参数
故在之后的RESTORE ALL中能顺利执行下去.
7. 未完待续。。
8. 望手下留情
相关文章推荐
- linux内核分析作业6:分析Linux内核创建一个新进程的过程
- Linux内核作业--分析Linux内核创建一个新进程的过程
- Linux内核分析6:分析Linux内核创建一个新进程的过程
- linux内核分析 第六周 分析Linux内核创建一个新进程的过程
- 第6节 分析Linux内核创建一个新进程的过程【Linux内核分析】
- Linux内核设计第六周学习总结 分析Linux内核创建一个新进程的过程
- linux内核分析第六周-分析Linux内核创建一个新进程的过程
- Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程
- [网易云课堂]Linux内核分析(六)—— 分析Linux内核创建一个新进程的过程
- Linux内核分析-分析Linux内核创建一个新进程的过程
- Linux内核及分析 第六周 分析Linux内核创建一个新进程的过程
- Linux内核分析课程-- 分析Linux内核创建一个新进程的过程
- Linux内核分析:分析Linux内核创建一个新进程的过程
- 《Linux内核--分析Linux内核创建一个新进程的过程 》 20135311傅冬菁
- Linux内核分析——分析Linux内核创建一个新进程的过程
- 作业六:分析Linux内核创建一个新进程的过程
- “Linux内核分析”实验报告(六)分析Linux内核创建一个新进程的过程
- 实验 六:分析linux内核创建一个新进程的过程
- 分析Linux内核创建一个新进程的过程(Linux)
- 分析Linux内核创建一个新进程的过程