zoukankan      html  css  js  c++  java
  • unix解释器文件详解

                exec执行普通文件和解释器文件的区别 2014-11-15 23:52:45

    分类: LINUX

    exec执行普通文件和解释器文件的区别

    ——lvyilong316

    1. 从一个问题开始

    首先要从项目中遇到的一个问题说起。编写一个python文件test.py,文件test.py内容如下:

    #! /usr/bin/python

    ....

    如果在命令行方式执行test.py的方式是:

    test.py -in inputfile -out outputfile;或python test.py -in inputfile -out outputfile;

    但是因为需要,用exec函数(这里使用execl)去调用这个python文件。在项目中是这样写的:

    execl(”test.py”,”-in”,”inputfile”,”-out”,”outputfile”,(char*)0);

        但执行结果并不是预想的test.py执行,而是启动了python交互程序,不知道是什么原因。因为一直以为如果写一个C程序,比如main。那么在命令行输入:main arg1 arg2执行的效果和execl(”main”,”arg1”,”arg2”,(char*)0)的效果应该是一样的。

    当然同时伴随我有另一个问题:

    execl(“usr/bin/python”,”test.py”,(char*)0);和输入命令”/usr/bin/python test.py”有什么区别?

    为了回答些问题,自己通过再反复看apue和做实验测试,终于一点一点明白了,下面就来一点一点的分析。

    2. 命令行执行程序和exec执行程序的区别

        首先我们来分析一下在命令行执行一个程序和通过exec函数执行程序有什么区别,或者说需要注意的地方(一下所有编写的文件都在/mnt/hgfs/VWShared/目录下)。

        编写程序foo.c如下,并编译为可执行文件foo。它打印参数列表(argv)的所有参数.

    l foo.c

    #include 

    int

    main(int argc,char* argv[])

    {

      int i;

      for(i=0;i<argc;i++)< span="">

        printf("argv[%d]: %s ",i,argv[i]);

      exit(0);

    }

    再编写main.c如下,将其编译为可执行文件main,它使用execl调用foo。

    l main.c

    #include 

    #include 

    int main(int argc,char* argv[])

    {

      int n=0;

      if( (n=execl("/mnt/hgfs/VWShared/foo",(char*)0))==-1 )

        {

           perror("execl error");

           exit(0);

        }

      exit(1);

    }

    直接在命令行下运行foo,结果如图1:

    图1

    运行main(通过execl运行foo)结果如图2:

    图2

    可以看出直接在命令行运行foo,则”./foo”被当做argv[0],但是通过exec运行foo发现并没有参数传入foo(程序没有任何输出),也就是说argc值为0。这是什么原因呢?我们知道argv存放的是传递给main函数的命令行参数,当在命令行键入”./foo”时,唯一的命令行参数”./foo”就被传入给main的argv了。所以直接在命令行运行foo就打印出唯一的参数”./foo”。

    那么execl的情况呢?首先看一下execl的原型:

    int execl(const char* pathname,const char* arg0,.../*(char*)0*/);

    注意到了吧,第一个参数是要执行的程序名,第二个参数才是要传入待执行程序的第一个参数,而上述main.c中没有第二个参数(这里说的是execl的第二个参数),也就是没有给foo传递任何参数,foo的参数表argv当然就是空了,或者说argc为0。

    通过这个例子我们要有以下认识:

    argv[0]不一定就是所执行程序的名称,确切的说它只是命令行的第一个参数,只是通常启动程序是在命令行键入程序名称启动的,所以程序的名称才成为argv[0]。但是也有情况argv[0]不是程序名称的,如:

    (1) 通过exec执行时,argv[0]是什么要视exec的参数来定。

    例如:我们将main中的execl语句改为:execl("/mnt/hgfs/VWShared/foo","xxxxx",(char*)0);

    再运行main,效果如图3:

    图3

    可以看到argv[0]变为了我们传入的参数”xxxxx”。

    (2) 通过程序别名启动时,argv[0]就是程序的别名。如我们给foo创建一个软连接sfoo,然后执行sfoo效果如图4:

    图4

    可以看出输出的argv[0]是./sfoo 而不是./foo,再次证明argv[0]是什么和程序名称无关,只是和传入的命令行第一个参数有关。

    补充:在创建上述软连接过程中遇到了一点小问题,不妨也在这里写下来:

    【问题】

    在编译VMware下的Linux系统对从Windows中共享过来的文件,进行编译的时候,遇到:ln: creating symbolic link XXXXXX : Operation not supported
    【解决办法】

    出现这类问题,主要是由于在编译的时候,要用ln去建立一些软链接,而这些文件是从Windows中,通过VMWare虚拟机共享进Linux的,而虽然此种操作在Linux系统中很常见,但Windows不支持,所以,编译会报错。比较方便的解决办法是先将文件考到linux的其他目录,再在其他非共享目录中创建软连接。另外还有个解决办法就是,在VMWare下的Linux中,建立Samba服务,然后新创建新samba用户和文件夹,然后在windows中就可以访问到该文件夹了。然后把在Linux中,从共享目录拷贝到你所要共享的samba目录中,这样,也可以实现我们所要的文件共享。此时在去编译这些代码的时候,由于是在Linux系统中的,所以就OK了。

    3. 解释器文件和解释器

    先解释两个概念;解释器文件和解释器。

    l 解释器文件:一种文本文件,开头通常是:#! pathname [option-argument];比较常见的是#! /bin/bash,shell脚本和python脚本都属于解释器文件。

    l 解释器:解释器文件第一行中pathname指定的程序,如bash。

    3.1 解释器文件的执行

        当执行(exec)"解释器"文件时,exec系统调用会识别这种文件,内核使调用exec函数的进程实际执行的并不是该"解释器文件",而是pathname指定的解释器。

    我们可以自己写一个解释器,如之前所写的foo.c:

    l foo.c

    #include 

    int

    main(int argc,char* argv[])

    {

      int i;

      for(i=0;i<argc;i++)< span="">

        printf("argv[%d]: %s ",i,argv[i]);

      exit(0);

    }

    编译成为foo然后保存在/mnt/hgfs/VWShared/。

    下面我们在自己写一个”解释器文件”——test:

    l test

    #!/mnt/hgfs/VWShared/foo

    3.1.1 通过命令行执行解释器文件

    直接在命令行中键入:”./test”,运行test,效果如图5。

    图5

    将test内容修改为:#!/mnt/hgfs/VWShared/foo  argA argB,再次在命令行运行test,效果如图6。

    图6

    通过这两个例子,可以看出命令行运行时当执行文件是”解释器文件”时,参数是如何传递给解释器的:

    (1) 通过执行”解释器文件”执行解释器,传递给解释器的第一个参数是解释器文件的pathname,即解释器的路径。

    (2) “解释器文件”中pathname后的可选参数(这里的argA,argB)如果存在的话会一起作为第二个参数传递给解释器。

    (3) “解释器文件”名称会作为下一个参数传递给解释器。

    3.1.2 通过execl执行解释器文件

         接下来通过execl执行解释器文件test,修改main中的exec语句如下:

    execl("/mnt/hgfs/VWShared/test","arg1",”arg2”,(char*)0));然后执行main,效果如图7。

    图7

        从这个例子可以了解当执行文件是解释器文件时,内核如何处理exec函数的参数及解释器文件第一行的可选参数。我们知道执行解释器文件实际是执行解释器,由解释器去读取解释器文件中的语句执行,而第一行的pathname以#开头在执行时会被当做注释忽略。下面就让我们分析一下最终传入解释器foo的参数都是什么。

    (1) argv[0]是该解释器文件的pathname;

    (2) argv[1]是该解释文件中的可选参数;

    (3) argv[2]是解释器文件本身名字;

    (4) argv[3]是execl出入的第二个参数(第一个参数是arg1)。

        那么问题出现了,我们传入execl的arg1去哪里了呢?其实这就是exec执行”解释器文件”和执行一般程序的不同之处:在执行一般程序时,execl(const char* pathname,const char* arg0,...,(char*)0)中的arg0会被当做执行程序(pathname)的第一个参数argv[0],而在执行解释器文件时,内核取execl调用中的pathname而非第一个参数(arg0)作为第一个参数传递给解释器,因为一般而言,第一个参数arg0通常是解释器文件的名字,而pathname包含了比arg0更多的信息(解释器文件的完整路径)。所以当execl执行解释器文件时第一个参数arg0是无效的。

    为了说明这个问题,我们再举一个例子,编写python文件pyth.py如下:

    l pyth.py:

    #! /usr/bin/python

    import sys

    for i in range(0,len(sys.argv)):

    print "argv[%d]: %s"%(i,sys.argv[i])

    它的功能和foo一样同样是打印每个命令行参数。我们分别将main中的execl语句改为:

    execl("/mnt/hgfs/VWShared/foo","arg1","arg2",(char*)0))和

    execl("/mnt/hgfs/VWShared/pyth.py","arg1","arg2",(char*)0)),对比execl一般程序(foo)和解释器文件(pyth.py)的效果如图8、9。

    图8.execl("/mnt/hgfs/VWShared/foo","arg1","arg2",(char*)0))结果

    图9.execl("/mnt/hgfs/VWShared/pyth.py","arg1","arg2",(char*)0))结果

    可以看出execl对于执行普通文件和解释器文件选取第一个参数是不同的。

     

    3.2 execl执行解释器文件和命令行执行解释器文件的不同

        我们上面已经看到execl("/mnt/hgfs/VWShared/pyth.py","arg1","arg2",(char*)0))的结果(图9),下面我们试一下命令行方式:pyth.py arg1 arg2,结果图10:

    图10

        可以看到结果和通过execl执行是有区别的,通过命令行执行解释器文件就像通过命令行执行普通程序一样,程序名称作为第一个参数,命令行后面依次作为后续参数。正因为对于解释器文件的execl方式和命令行方式执行时选取第一个参数的方式不同,所以对于解释器文件a.py:

    (1) 在命令行输入:./a.py arg1 arg2;

    (2) execl("./a.py","arg1","arg2",(char*)0));

    (3) execl("./a.py",”xxx”,"arg1","arg2",(char*)0));

    方式(1)和方式(2)不等价,因为方式(1)中arg1会被当做第二个参数传递给解释器,而方式(2)中arg2会被当做第二个参数传递给解释器。方式(1)和方式(3)是等价的。

    对于普通文件foo:

    (1) 在命令行输入: ./foo arg1 arg2;

    (2) execl("./foo","arg1","arg2",(char*)0))

    方式(1)和方式(2)是等价的。

    4. 回答开始的问题

        为了达到命令行方式:test.py arg1 arg2的效果,使用execl("test.py","arg1","arg2",(char*)0))肯定是不行的,因为arg1会被忽略,提示缺少参数。正确的方式是:execl("test.py",”xxx”,"arg1","arg2",(char*)0)),这里”xxx”代表任意字符串,不过一般会使用解释器文件名,即”test.py”。

         为了达到命令行方式:python test.py arg1 arg2的效果,使用execl("python",”test.py”,"arg1","arg2",(char*)0))也是不行的,因为test.py会被忽略,arg1会被当做第一个参数传给python解释器。正确方式是:

    execl(“python",”xxx”,”test.py”,"arg1","arg2",(char*)0)),这里”xxx”代表任意字符串,不过一般会使用解释器文件名,即”test.py”。

  • 相关阅读:
    [置顶] windows player,wzplayerV2 for windows
    wzplayer 近期将会支持BlackBerry和WinPhone8
    wzplayerEx for android(真正硬解接口,支持加密的 player)
    ffmpeg for ios 交叉编译 (支持i686 armv7 armv7s) 包含lame支持
    ffmpeg for ios 交叉编译 (支持i686 armv7 armv7s) 包含lame支持
    编译cegcc 0.59.1
    wzplayer 近期将会支持BlackBerry和WinPhone8
    wzplayerEx for android(真正硬解接口,支持加密的 player)
    windows player,wzplayerV2 for windows(20140416)更新
    编译cegcc 0.59.1
  • 原文地址:https://www.cnblogs.com/fengting/p/5751210.html
Copyright © 2011-2022 走看看