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] = '0';
// 找出buf里第一次出现换行字符的地址,没有则返回NULL
if (cp = strchr(buf, 'n')) {
// 更新换行字符为0,表示字符串结束
*cp = '0';
// cp指向文件的第一个字符