九阳神功爱喝茶 发表于 2015-06-16 19:19

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;

还请各位多多指导。


wangcong02345 发表于 2016-12-21 17:16

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, &current->thread.i387);
[*]
[*]    return 0;
[*]}

为啥从博客复制过来就变成这个样子了?

mordorwww 发表于 2016-12-25 16:24

顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶 顶

nswcfd 发表于 2016-12-26 16:06

有图就好理解多了,赞!

aweii 发表于 2021-05-04 09:52

本帖最后由 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]
查看完整版本: copy_thread函数的一些不理解的地方,求助大家?