日期:2014-05-16  浏览次数:21099 次

shell程序、fork()函数、execve()函数之间什么关系?
目前在看linux内核代码方面的东西,目前主要是文件系统FS这一块的内容。
在exec.c源码文件中,有一个函数 do_execve() 这个函数是会被 “系统调用”函数 sys_execve()会调用的,而sys_execve()是某进程通过“中断调用过程”(int 0x80, 功能号为__NR__EXECVE)被调用的,即sys_execve()函数的实现是直接通过汇编写的,如下:
####这是 sys_execve() 系统调用。取中断调用程序的代码指针作为参数调用C函数(do_execve())
[本人附加说明:sys_execve()被编译中汇编后,在汇编代码中对应的该函数“入口地址”就是指 标号“_sys_execve”处的地址]
# do_execve()在(fs/exec.c,182)。
.align 2
_sys_execve:
lea EIP(%esp),%eax
pushl %eax
call _do_execve
addl $4,%esp # 丢弃调用时压入栈的EIP值
ret

现在问题是,在linux下,如果要执行一个特定的可执行程序(假如是 hello 可执行程序),我们一般会在命令行中直接输入 hello 后回车,这时命令行所在的进程(即父进程,这个父进程一般应该是 /bin/sh,也可能是别的shell程序,如csh,bash等,但这里就以 sh 为例),然后这个父进程会通过fork()函数“创建”出一个子进程(假如以B表示该子进程吧)来,然后在B中执行 hello 这个程序(即子进程会调用execve()函数加载 hello 程序,然后在子进程空间中“转向”去执行这个程序),这样子进程B就是hello程序的进程了。
这里就有以下问题:
1、sh本身是可执行程序吗?(很多地方说,sh是解释程序)如果是解释程序?sh有源码吗?或是说 sh会解释命令行中的参数信息吧(因为 do_execve()中的参数莫名其妙地被传入了)。 sh做了些什么工作呢?
2、子进程B是在 sh 程序内部被 fork() 出来的吗?也就是说,sh程序会调用 fork() 函数吗?还做了其他什么工作呢?
3、子进程B在执行过程中,会在什么时候调用“系统调用函数”sys_execve()呢? (调用方式可能是通过“系统中断调用”int 0x80,功能号为__NR_execve (值为11)也可能是直接通过 execve() 而“转向”调用“系统中断调用”)
4、init()函数中fork()出了一个子进程,该子进程负责execve() sh解释程序,即sh程序也是通过 execve() 函数完成被加载及运行的,sh一直在运行,所以我们可以看到命令行界面的。

init()程序中执行sh的代码如下所示:

[说明:前面的省略了]
// 关闭了句柄0(stdin),以只读方式打开/etc/rc 文件,并执行/bin/sh 程序,所带参数和
  
// 环境变量分别由argv_rc 和envp_rc 数组给出。参见后面的描述。
  
if (!(pid = fork ()))
{
  close (0);
  if (open ("/etc/rc", O_RDONLY, 0))
_exit (1);// 如果打开文件失败,则退出(/lib/_exit.c,10)。
  execve ("/bin/sh", argv_rc, envp_rc); // 装入/bin/sh 程序并执行。

  _exit (2); // 若execve()执行失败则退出(出错码2,“文件或目录不存在”)。
   
}
[后面省略了]

即/bin/sh这个解释程序一直在运行着,然后等待着用户在命令行中输入信息。
假如就是 hello 程序,这样就回到了前面的问题了。

赵博士的《linux内核完全剖析》一书中在exec.c程序中有这样的描述:
“当一个程序使用fork()函数创建了一个子进程时,通过会在该子进程中调用exec()函数簇之一以加载执行另一个新程序。”
这里的“exec()函数簇之一”应该就是指:execve() 函数了,而这个execve()函数是通过宏定义来实现的,宏定义中就是
通过汇编完成“系统中断调用”,对应的中断向量号是0x80,采用的调用功能号是 __NR__EXECVE ,代码如下:

通过宏来完成对execve()函数的定义:

_syscall3 (int, execve, const char *, file, char **, argv, char **, envp)

#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ( "int $0x80" \
: "=a" (__res) \
: "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b)), "d" ((long)(c))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
return -1; \
}

所以调用 execve() 函数时,就会调用用C声明的sys_execve()函数,而sys_execve()函数的实现是直接通过汇编写的(开头部分)。


------解决方案--------------------
sh只是一个命令集合而已 它会被shell解析 执行 实际的执行过程还是一个一个命令进行的
当然其中很多变量是由shell解析出来然后执行的
------解决方案--------------------
引用楼主 xufeiyunwindows 的帖子:
1、sh本身是可执行程序吗?(很多地方说,sh是解释程序)如果是解释程序?sh有源码吗?或是说 sh会解释命令行中的参数信

息吧(因为 do_execve()中的参数莫名其妙地被传入了)。 sh做了些什么工作呢?
2、子进程B是在 sh 程序内部被 fork() 出来的吗?也就是说,sh程序会调用 fork() 函数吗?还做了其他什么工作呢?
3、子进程B在执行过程中,会在什么时候调用“系统调用函数”sys_execve()呢? (调用方式可能是通过“系统中断调用”int

0x80,功能号为__NR_execve (值为11)也可能是直接通过 execve() 而“转向”调用“系统中断调用”)
4、init()函数中fork()出了一个子进程,该子进程负责execve() sh解释程序,即sh程序也是通过 execve() 函数完成被加载

及运行的,sh一直在运行,所以我们可以看到命令行界面的。


------解决方案--------------------
up
------解决方案--------------------
你需要把shell(如bash)跟shell程序(*.bsh)分清.

fork出来后,exec是子进程内调用的,父进程怎么可能去替换子进程的程序呢?这里也需要把进程跟可执行程序分清。

你的shell程序是否要运行完成才返回到shell来是看你是怎么运行的,哪果是加&运行就不需要等待,这里的区别只是父shell会不会wait子进程的退出状态的区别而已。