用户栈位于进程空间的最高部份.那进程初始化时,用户栈存放的是什么呢?是参数.进程在执行时会到栈中去取运行时所需的参数.这里所谓的参数包含了可执行程序所带的参数和环境变量.例如:在shell上执行”echo hello,eric” .echo程序带有二个参数.argv[0] = “echo”,argv[1] = “hello,eric”即第一个参数为程序名称.其后的参数分别是运行进程所带的参数.当然,在上面这个例子中没有列出环境变量.一般的.在参数后面都跟了一个NULL.表示参数已经结束了,在上例中argv[1]后面的一个字节是NULL.如下图所示:
这样程序在运行的时候就可以方便的确定参数及环境变量的个数. 现在,我们可以分析代码了.bprm_mm_init()是bprm的初始化函数,我们跟踪进去看它是怎么样初始化的.int bprm_mm_init(struct linux_binprm *bprm)
重点是在__bprm_mm_init():
{
int err;
struct mm_struct *mm = NULL;
//分配一个mm
//mm_alloc我们在进程创建的时候已经分析过了,值得注意的是,它会调用mm_init()来为
//进程的用户空间建立PGD->PMD映射
bprm->mm = mm = mm_alloc();
err = -ENOMEM;
if (!mm)
goto err;
err = init_new_context(current, mm);
if (err)
goto err;
//初始化bprm->mm
err = __bprm_mm_init(bprm);
if (err)
goto err;
return 0;
err:
if (mm) {
bprm->mm = NULL;
mmdrop(mm);
}
return err;
}static int __bprm_mm_init(struct linux_binprm *bprm)
上面的操作看起来比较隐晦,我们把它的操作用下面的图表示:
{
int err = -ENOMEM;
struct vm_area_struct *vma = NULL;
struct mm_struct *mm = bprm->mm;
//分配一个VMA
bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
if (!vma)
goto err;
down_write(&mm->mmap_sem);
vma->vm_mm = mm;
//STACK_TOP_MAX:进程用户空间的最高值
//对应进程的栈顶
vma->vm_end = STACK_TOP_MAX;
vma->vm_start = vma->vm_end - PAGE_SIZE;
vma->vm_flags = VM_STACK_FLAGS;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
//将VM插入mm表示的进程空间结构
err = insert_vm_struct(mm, vma);
if (err) {
up_write(&mm->mmap_sem);
goto err;
}
mm->stack_vm = mm->total_vm = 1;
up_write(&mm->mmap_sem);
//bprm->p:用户栈的栈指针
bprm->p = vma->vm_end - sizeof(void *);
return 0;
err:
if (vma) {
bprm->vma = NULL;
kmem_cache_free(vm_area_cachep, vma);
}
return err;
}