一直以来都对execve到底做了什么,总是犯迷糊,原来看Linux内核设计的艺术,这部分讲解的非常不细致,这次结合赵博士的书,重新理解了这部分代码。
首先列出代码,如下:
if (!(pid=fork())) {//进程1创建进程2
close(0);
if (open("/etc/rc",O_RDONLY,0))
_exit(1);
execve("/bin/sh",argv_rc,envp_rc);
_exit(2);
}
进程1创建进程2,进程2的页目录表及页表如图1,页目录表项是第32位,由于页目录表从内核0x0的位置,所以进程2的页目录项的位置为32,由于每个页目录项所占的字节数为4,所以内存地址为128。
图 1
此时用命令行,查看内核地址为128的数据。0xffa027,就是进程2页表的首地址 。
图 2
0xffa000开始存放的进程2的页表项,如下图:
图 3
下面看真正的execve,代码如下:
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
{
struct m_inode * inode;
struct buffer_head * bh;
struct exec ex;
unsigned long page[MAX_ARG_PAGES];//MAX_ARG_PAGES为32
int i,argc,envc;
int e_uid, e_gid;
int retval;
int sh_bang = 0;
unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;//4096*32-4=131068=1FFFC
if ((0xffff & eip[1]) != 0x000f)
panic("execve called from supervisor mode");
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
page[i]=0;
if (!(inode=namei(filename))) //找到/bin/sh的i节点
return -ENOENT;
argc = count(argv);//参数的数量
envc = count(envp);//环境变量的数量
restart_interp:
.....
if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {//读取第一块的数据
retval = -EACCES;
goto exec_error2;
}
ex = *((struct exec *) bh->b_data); //赋值给文件头
.....
if (!sh_bang) {
p = copy_strings(envc,envp,page,p,0);
p = copy_strings(argc,argv,page,p,0);//最后返回的p是131068减去参数和环境变量的字节数,堆栈的指针。目前page数组中,page[31]已经是一个新申请页面的地址了。
i