通过do_execve源码分析程序的执行(上)(基于linux0.11)
- 2019 年 10 月 4 日
- 筆記
execve函数是操作系统非常重要的一个函数,他使得程序变成进程成为可能。下面我们通过do_execve的实现,了解一下程序变成进程的过程。首先do_execve是一个系统调用。之前分析过系统调用的过程。这里就不详细说了。直接从sys_execve函数开始。
_sys_execve: lea EIP(%esp),%eax pushl %eax call _do_execve addl $4,%esp ret
执行_do_execve函数前,先看看这时候的内核栈。

在这里插入图片描述 下面开始分析do_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]; int i,argc,envc; int e_uid, e_gid; int retval; int sh_bang = 0; unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4; // eip指向系统调用前的eip,eip[1]则指向cs,判断一下这时候的cs是不是用户的cs 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))) /* get executables inode */ return -ENOENT; // 计算环境变量和参数个数 argc = count(argv); envc = count(envp); restart_interp: if (!S_ISREG(inode->i_mode)) { /* must be regular file */ retval = -EACCES; goto exec_error2; } i = inode->i_mode; // 设置了uid则执行的时候uid是设置的uid,否则是用户的有效id e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; // 相等说明该文件是该用户创建的,则判断user位的权限 if (current->euid == inode->i_uid) i >>= 6; // 同上,判断组权限 else if (current->egid == inode->i_gid) i >>= 3; /* else 判断 other的权限 */ if (!(i & 1) && !((inode->i_mode & 0111) && suser())) { retval = -ENOEXEC; goto exec_error2; } // 读第一块数据进来 if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { retval = -EACCES; goto exec_error2; } // 前面是执行文件的头,包括一些元数据 ex = *((struct exec *) bh->b_data); /* read exec-header */ // 是脚脚本文件,不是编译后的文件,sh_bang控制只会进入一次 if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { /* * This section does the #! interpretation. * Sorta complicated, but hopefully it will work. -TYT */ char buf[1023], *cp, *interp, *i_name, *i_arg; unsigned long old_fs; // 把#!之外的字符复制到buf strncpy(buf, bh->b_data+2, 1022); brelse(bh); iput(inode); buf[1022] = '