Linux中exec系列函数的应用

 分类: Server

这段时间在研究linux中用户登陆和shell执行程序的原理。我们知道,shell命令分为内部命令和外部命令,内部命令有诸如cd,history,exit,echo等,常见的外部命令有ls,ping,netstat等,通过type命令可以查看一个命令是内部命令还是外部命令。当执行的命令是一个内部命令是,shell直接执行;那么shell是如何执行一个外部命令的呢?

Shell本身也是一个程序,程序之间调用的方式也有很多,例如popen,但shell成功的执行一个外部命令,要满足以下条件:
1,首先是shell能调用这个程序,这个是必须的前提。
2,第二是shell必须要等待外部程序执行完毕才能返回。
3,第三是外部程序执行时,shell必须要把所有的执行权限交给外部命令,且外部命令需要继承shell的状态,如当前工作目录,控制终端,根目录等等。

实际上在linux中,shell执行外部命令都是以子进程的方式执行,在通过pstree看到linux的进程树,如下:

[root@home ~]# pstree
systemd-+-2*[agetty]
        |-avahi-daemon---avahi-daemon
        |-avahi-dnsconfd
        |-crond
        |-dbus-daemon
        |-haveged
        |-ifplugd
        |-master---pickup
        |-python
        |-rngd
        |-sshd-+-3*[sshd]
        |      `-sshd---bash---pstree
        |-systemd---(sd-pam)
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        `-wpa_supplicant

可以看到pstree属于bash的子进程。那么,shell执行外部命令的原理是什么呢?
当shell执行一个外部命令是,第一步是fork出一个子进程,shell获取传递进来的参数,然后子进程调用一种exec函数执行另一个程序,子进程执行的程序完全替换为新程序,因为exec不创建新的进程,所以前后的进程ID不会改变。下面代码可以用来模拟shell执行外部程序:

#!/usr/bin/env python
import os,sys

if sys.argv[1:]:
        pid=os.fork()
else:
        sys.exit(0)

if not pid:
        filename=sys.argv[1]
        argv1=filename
        argvs=",".join(sys.argv[2:])
        os.execlp(filename,argv1,argvs)

os.waitpid(pid,0)

把此文件保存为ba,增加执行权限,然后运行./ba ping www.hitoy.org,程序执行正常,运行pstree,发现有python产生一个ping的子程序。

Linux上,一个程序调用另外程序经常会采用这种先fork然后exec的方式。再来看一下exec,exec是linux上的一组函数,一共有6个,如下:

int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execle(const char *path, const char *arg, …, char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

需要注意的是,上面的函数中,第一个参数path必须为路径名,filename可以是路径名也可以是文件名,当为文件名时,系统会在PATH环境变量中查找这个文件。另外一点需要注意的是,传递的arg值,必须从第0个参数开始都传递,也就是必须连同cmdline一起把所有参数传递给这个函数。

发表回复

评论列表:

翱翔9天
翱翔9天
强大的Python,我要学!!
回复此留言