copy_thread函数的一些不理解的地方,求助大家?
本帖最后由 九阳神功爱喝茶 于 2015-06-16 19:28 编辑小弟研究copy_thread函数的时候遇到些问题,还要麻烦各位前辈多多指导,万分感谢。问题见红色标注的地方
int copy_thread(unsigned long clone_flags, unsigned long sp,
unsigned long unused,
struct task_struct *p, struct pt_regs *regs)
{
struct pt_regs *childregs;
struct task_struct *tsk;
int err;
//这儿是在内核栈的栈顶开辟8个字节的内存空间,我很不理解这里有什么作用?
childregs = task_pt_regs(p);
//保存父寄存器的值到自己存器中;
*childregs = *regs;
//修改子进程寄存器的相关值;childregs占用了17*4个字节,但是只开辟了8字节的内核空间,这样的话不是破坏了原来的thread_info结构体的内容了吗?
childregs->ax = 0;
childregs->sp = sp;
p->thread.sp = (unsigned long) childregs;
p->thread.sp0 = (unsigned long) (childregs+1);
p->thread.ip = (unsigned long) ret_from_fork;
task_user_gs(p) = get_user_gs(regs);
tsk = current;
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
IO_BITMAP_BYTES, GFP_KERNEL);
if (!p->thread.io_bitmap_ptr) {
p->thread.io_bitmap_max = 0;
return -ENOMEM;
}
set_tsk_thread_flag(p, TIF_IO_BITMAP);
}
err = 0;
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS)
err = do_set_thread_area(p, -1,
(struct user_desc __user *)childregs->si, 0);
if (err && p->thread.io_bitmap_ptr) {
kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0;
}
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
p->thread.ds_ctx = NULL;
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
p->thread.debugctlmsr = 0;
return err;
}
代码里面的文字不能标示红色,只能在最后写下问题了:
//这你是在内核栈的栈顶开辟8个字节的内存空间,我很不理解这里有什么作用?
childregs = task_pt_regs(p);
第二个是:
//修改子进程寄存器的相关值;childregs占用了17*4个字节,但是只开辟了8字节的内核空间,这样的话不是破坏了原来的thread_info结构体的内容了吗?
childregs->ax = 0;
childregs->sp = sp;
还请各位多多指导。
cu发博文进编译页面无法显示文字内容
本帖最后由 wangcong02345 于 2016-12-21 17:17 编辑1. 楼主的这几个问题其实是一个问题,就是childreg指代的内存是什么的问题。
2. 上面 childregs = task_pt_regs(p);实际上是--> childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1;
也就是说childregs指向 “子进程的栈顶 减去 一个sizeof(struct pt_regs)的大小 的地方”,如下图所示:
http://blog.chinaunix.net/attachment/201612/21/26009923_1482310852W10z.png
上图出自《linux内核情景分析》P306
3. 其它的都好理解了:
这里参数p=0xf7dee000,在do_fork中新分配的2页内存用作子进程的task_struct
[*]int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
[*] unsigned long unused, struct task_struct * p, struct pt_regs * regs)
[*]{
[*] struct pt_regs * childregs;
[*]//这儿稍不留神就会看错-->这儿是先转为struct pt_regs后再减1
[*]//这是在栈顶留出一个struct pt_regs的大小
[*] childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1;
[*]//先将父进程的全部regs复制过来,然后再作调整
[*] struct_cpy(childregs, regs); //即memcpy(childregs, regs, sizeof(*(regs)));
[*] childregs->eax = 0; //child的eax=0,这也就是为什么fork后子进程返回0的原因
[*] childregs->esp = esp; //这个esp是在sys_clone时的regs.ecx=0x78
[*]
[*] p->thread.esp = (unsigned long) childregs; //执行后p->thread.esp=0xf7deffc4=(p-sizeof(pt_regs))
[*] p->thread.esp0 = (unsigned long) (childregs+1);//执行后p->thread.esp0=0xf7df0000=(p),即将两页内存的最高端作为栈顶
[*]
[*] p->thread.eip = (unsigned long) ret_from_fork; //下次调度时子进程执行的命令是ret_form_fork
[*]
[*] savesegment(fs,p->thread.fs);
[*] savesegment(gs,p->thread.gs);
[*]
[*] unlazy_fpu(current);
[*] struct_cpy(&p->thread.i387, ¤t->thread.i387);
[*]
[*] return 0;
[*]}
为啥从博客复制过来就变成这个样子了?
顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 有图就好理解多了,赞! 本帖最后由 aweii 于 2021-05-04 10:12 编辑
内核堆栈顶部留出8字节,是保证内核态下发生中断时,regs->esp、regs->xss是可访问的。否则,由于此时ess、esp不压栈(ring0->ring0),所以*regs实际包含了thread_union以外的8字节。我观察了下,2.6.11以前的内核都没有这个8字节预留,当然也没出什么问题(因为都在内核的892M空间内,有映射)。2.6.24有了这个task_pt_regs(p),也许是防患于未然吧。
页:
[1]