注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

从内核对脚本文件加载看脚本语言一些特征  

2012-05-28 22:13:28|  分类: 脚本语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、脚本类可执行文件
脚本类可执行文件是内核可识别的一种可执行文件格式,但是事实上脚本语言本身通常不具有自我执行能力,它和我们常见的ELF文件不同,脚本文件本质上是一种指令描述文件,它里面没有机器可直接识别的机器指令,这些内容只能由脚本语言对应的解析器来代劳。例如perl的脚本由perl来解析,shell脚本由bash解释,以及众多的awk 、sed等脚本,它们将不同的Unix程序粘合在一起。不管如何,这些脚本文件都会在自己的开始通过#!来表示自己是一个脚本文件,紧接着跟上自己需要使用的解析器可执行程序的路径,然后内核在执行这个脚本的时候事实上是转发给了脚本解析器,而脚本本身是作为了解析器的一个参数来使用。
二、以#为注释
由于所有的脚本语言来说,内核需要通过文件开始的#!(she-bang ,其中的she应该是最为原始的shell的缩写吧)来确定它是一个脚本文件,而这个句子对于脚本语言本身来说必须是一个无关紧要的内容,也即是我们通常所说的注释。因为如果说脚本语言不把‘#’作为注释,那么当这个脚本作为单独的输入文件提供给解析器来运行的时候,它会出现语法解析错误,或者说是一些意料之外的状况,所以说通用的脚本语言必须以‘#’作为注释,或者说‘#’至少是一个注释符号,当然也可以有其它的注释符号。这一点对于常见的脚本语言,bash、perl、awk、sed均适用,甚至是对于一般不是作为脚本语言的Makefile也适用。
三、非选项参数为输入脚本文件
这一点同样要 结合内核的实现来看。内核如果判断出可执行程序是一个脚本文件的话,它会对这个exec启动的命令做特殊处理,而判断是否为可执行文件的方法就是使用第二节中所说的#!来判断。如果通过判断,那么内核会把用户输入可执行文件的名称(脚本文件)作为参数传递给脚本中描述的解析器,然后重新执行这条命令。这里可以看到的是,内核就是假设解析器对于所有的非选项型参数(这里选项型参数通俗的说就是通过-xx引导的命令行参数,也叫做option)。所以大部分的脚本语言为了支持能够最为简单的由内核加载,它就必须把非选项参数作为脚本文件的输入,而不是通过某些选项来支持。这一点对于shell、perl同样适用,其它的没有测试;但是这一点对于make的语法来说是不成立的,因为make的脚本文件默认是GNUMakefile Makefile makefile顺序搜索,如果都不是,那么需要通过-f特别指明脚本文件的命令,所以这一点也是为什么makefile通常不作为一个单独脚本执行的原因。
四、如何让makefile脚本直接运行
事实上,一个脚本文件不仅可以指明自己的解析器,还可以指明传递给解析器的参数,更进一步说,脚本文件第一行解析器之后的所有内容都会原封不动的透传给脚本解析器,作为它的参数。既然makefile需要通过-f指定它的文件名称,那么我们就可以在makefile的最开始注明,#!/bin/make之后再加上 -f 来指明文件名称。但是这里就有一个问题,假设说我通过一个makefile的脚本,make的目标是 Something,然后脚本文件名为DoSomething.mk,然后文件开始为
#!/bin/make -f
那么,内核最终合并并启动的命令是如何组织参数的,是
/bin/make -f DoSomething.mk Something
还是
/bin/make -f  Something DoSomething.mk
显然后一种是错误的。所以先测试一下
[tsecer@Harry MakeScript]$ ls -l
total 4
-rwxrwxrwx. 1 root root 51 2012-05-28 22:04 DoSomething.mk
[tsecer@Harry MakeScript]$ cat DoSomething.mk
#!/usr/bin/make -f
default:
    echo happily executed
[tsecer@Harry MakeScript]$ ./DoSomething.mk
echo happily executed
happily executed
[tsecer@Harry MakeScript]$
大家可以看到,makefile也可以直接运行(相对于make -f 形式),没有问题。
五、内核相关处理
1、脚本文件判断
static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
    if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang))
        return -ENOEXEC;
    /*
     * This section does the #! interpretation.
     * Sorta complicated, but hopefully it will work.  -TYT
     */

    bprm->sh_bang++;
2、参数构造
同一个函数中
    strcpy (interp, i_name);
    /*
     * OK, we've parsed out the interpreter name and
     * (optional) argument.
     * Splice in (1) the interpreter's name for argv[0]
     *           (2) (optional) argument to interpreter
     *           (3) filename of shell script (replace argv[0])
     *
     * This is done in reverse order, because of how the
     * user environment and arguments are stored.
     */
    remove_arg_zero(bprm);
    retval = copy_strings_kernel(1, &bprm->interp, bprm);
    if (retval < 0) return retval;
    bprm->argc++;
    if (i_arg) {
        retval = copy_strings_kernel(1, &i_arg, bprm);
        if (retval < 0) return retval;
        bprm->argc++;
    }
    retval = copy_strings_kernel(1, &i_name, bprm);
    if (retval) return retval;
    bprm->argc++;
用户态进程参数构造过程,自上而下内容依次为 脚本文件名称、脚本中解析器之后参数列表、解析器名称,而这之上还有传递给脚本的原始的命令行选项,例如刚才的例子中还可以有./DoSomething.mk defulat。
  评论这张
 
阅读(694)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017