• 【原创】控制perl和python脚本执行过程中脚本文件是否关闭的方法


    引子

      跟踪perl和python脚本对文件的访问,实际过程中,perl和python解析器在解析完脚本后,直接关闭了

    脚本文件,在进程中查询不到是访问文件的脚本文件名称。

    shell、perl和python脚本执行过程

    bash脚本执行过程

    脚本内容:

    #!/usr/bin/env bash
    
    echo `date`" hello world!" >> /root/log.txt

    使用strace跟踪脚本执行过程,为了节省篇幅,只保留一些关键执行过程:

    # strace -q ./test.sh
    execve("./test.sh", ["./test.sh"], [/* 35 vars */]) = 0    ##启动脚本进程
    ......
    open("./test.sh", O_RDONLY)             = 3              ##打开脚本文件
    ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff37bb77d0) = -1 ENOTTY (Inappropriate ioctl for device)
    lseek(3, 0, SEEK_CUR)                   = 0
    read(3, "#!/usr/bin/env bash echo `date`"..., 80) = 66
    lseek(3, 0, SEEK_SET)                   = 0
    getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
    fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
    dup2(3, 255)                            = 255                ##复制已打开的脚本文件描述符为255
    close(3)                                = 0                       ##关闭已打开的脚本文件描述符3
    ......
    read(255, "#!/usr/bin/env bash echo `date`"..., 66) = 66     ##读取test.sh脚本内容
    .....
    open("/root/log.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3  ## 执行test.sh脚本中打开日志文件的动作
    ....

    read(3, "Sun Feb  3 16:15:31 CST 2019 ", 128) = 29  ## 获取date命令结果
    read(3, "", 128)                        = 0
    .....
    write(1, "Sun Feb 3 15:51:56 CST 2019 hell"..., 41) = 41    ## 执行test.sh脚本中写日志文件的动作
    dup2(10, 1)                             = 1
    fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
    close(10)                               = 0
    rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
    read(255, " ", 66)                     = 1
    rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
    read(255, "", 66)                       = 0
    exit_group(0)                           = ?    ##退出shell脚本执行,这里会关闭所有打开的文件描述符

    ksh脚本执行过程:

    #!/usr/bin/env ksh
    
    echo `date`" hello world!" >> /root/log.txt

     strace -q ./test.sh
    execve("./test.sh", ["./test.sh"], [/* 35 vars */]) = 0   ##启动脚本进程
    ......
    open("./test.sh", O_RDONLY)             = 3             ##打开脚本
    fstat(3, {st_mode=S_IFREG|0755, st_size=65, ...}) = 0
    fcntl(3, F_DUPFD, 10)                   = 10              ##复制脚本文件的描述符
    close(3)                                = 0                         ## 关闭脚本文件

    /*

    open("./test.sh", O_RDONLY)             = 3
    fstat(3, {st_mode=S_IFREG|0755, st_size=65, ...}) = 0
    fcntl(3, F_DUPFD, 10)                   = 11   ##有的系统得出的结果是11
    close(3)                                = 0
    fcntl(11, F_SETFD, FD_CLOEXEC)          = 0

    */
    ......
    read(10, "#!/usr/bin/env ksh echo `date`""..., 8192) = 65   ##读脚本文件内容
    read(10, "", 8192)                      = 0
    brk(0xf09000)                           = 0xf09000       
    ......
    read(3, "Sun Feb  3 16:24:53 CST 2019 ", 8192) = 29   ##获取date命令内容
    read(3, "", 8192)                       = 0
    close(3)                                = 0
    ......
    write(3, "Sun Feb  3 16:24:53 CST 2019 ", 29) = 29   
    ......
    open("/root/log.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3  ##打开日志文件
    ......
    write(1, "Sun Feb 3 16:24:53 CST 2019 hell"..., 41) = 41    ##写日志文件
    lseek(1, 0, SEEK_CUR)                   = 492
    ......
    exit_group(0)                           = ?             ##退出shell脚本执行,这里会关闭所有打开的文件描述符

    perl脚本执行过程

    1 #!/usr/bin/env perl
    2 
    3 $datestring = localtime();
    4 
    5 open(logfile,">>/mnt/newlog3.txt") or die "Cant't open /mnt/newlog3.txt";
    6 print logfile "$datestring hello world! I am Perl!
    ";
    7 close(logfile);

    # strace /usr/bin/perl ../hello.pl
    execve("/usr/bin/perl", ["/usr/bin/perl", "../hello.pl"], [/* 62 vars */]) = 0  ##执行hello.pl脚本
    ......
    open("../hello.pl", O_RDONLY)           = 3    ##以下代码是打开hello.pl脚本并读取脚本内容(包含脚本解析)
    ......
    read(3, "#!/usr/bin/env perl $datestring"..., 4096) = 194
    read(3, "", 4096)                       = 0
    close(3)                                = 0            ## 关闭已经打开的hello.pl脚本
    open("/etc/localtime", O_RDONLY)        = 3     ## 打开时间文件,获取时间:对应脚本第三行
    fstat(3, {st_mode=S_IFREG|0644, st_size=414, ...}) = 0
    fstat(3, {st_mode=S_IFREG|0644, st_size=414, ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb976b7b000
    read(3, "TZif233"..., 4096) = 414
    lseek(3, -249, SEEK_CUR)                = 165
    read(3, "TZif233"..., 4096) = 249
    close(3)                                = 0
    munmap(0x7fb976b7b000, 4096)            = 0
    open("/mnt/newlog3.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3    ##打开日志文件  /mnt/newlog3.txt:对应脚本第5和6行
    lseek(3, 0, SEEK_END)                   = 389
    ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7ffd3b444fb0) = -1 ENOTTY (Inappropriate ioctl for device)
    lseek(3, 0, SEEK_CUR)                   = 389
    fstat(3, {st_mode=S_IFREG|0644, st_size=389, ...}) = 0
    fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
    write(3, "Mon Feb 11 18:31:40 2019 hello w"..., 49) = 49
    close(3)                                = 0   ##关闭日志文件  /mnt/newlog3.txt
    exit_group(0)                           = ?

    python脚本执行过程

     1 #!/usr/bin/python
     2 
     3 import os
     4 import datetime,time
     5 
     6 
     7 with open("/mnt/newlog3.txt", 'a+') as fp:
     8     cur_date=datetime.datetime.now()
     9     cur_date_str=cur_date.strftime("%Y-%m-%d %H:%M:%S")    
    10     fp.write("{0} Hello world! I am python!
    ".format(cur_date_str))
    11     fp.close()

    # strace ./hello.py
    execve("./hello.py", ["./hello.py"], [/* 62 vars */]) = 0   ##执行hello.py脚本
    ......
    getcwd("/root/liangjs", 4096)           = 14
    lstat("/root/liangjs/hello.py", {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    stat("./hello.py", {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    open("./hello.py", O_RDONLY)            = 3                  ##打开hello.py脚本并解析该python脚本
    fstat(3, {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f67ad9e8000
    fstat(3, {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    lseek(3, 0, SEEK_SET)                   = 0
    read(3, "#!/usr/bin/python import os imp"..., 241) = 241
    read(3, "e_str)) fp.close() ", 4096) = 22
    close(3)                                = 0
    munmap(0x7f67ad9e8000, 4096)            = 0
    stat("./hello.py", {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    open("./hello.py", O_RDONLY)            = 3
    fstat(3, {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff46f2d390) = -1 ENOTTY (Inappropriate ioctl for device)
    fstat(3, {st_mode=S_IFREG|0750, st_size=263, ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f67ad9e8000
    lseek(3, 0, SEEK_CUR)                   = 0
    read(3, "#!/usr/bin/python import os imp"..., 4096) = 263
    lseek(3, 263, SEEK_SET)                 = 263
    brk(0x6c7000)                           = 0x6c7000
    read(3, "", 4096)                       = 0
    brk(0x6be000)                           = 0x6be000
    brk(0x6bc000)                           = 0x6bc000
    close(3)                                = 0                           ##关闭已经打开的hello.py脚本文件
    munmap(0x7f67ad9e8000, 4096)            = 0
    ......
    open("/etc/localtime", O_RDONLY)        = 4                    ##获取时间信息
    fstat(4, {st_mode=S_IFREG|0644, st_size=414, ...}) = 0
    fstat(4, {st_mode=S_IFREG|0644, st_size=414, ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f67ad9e8000
    read(4, "TZif233"..., 4096) = 414
    lseek(4, -249, SEEK_CUR)                = 165
    read(4, "TZif233"..., 4096) = 249
    close(4)                                = 0
    munmap(0x7f67ad9e8000, 4096)            = 0
    stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=414, ...}) = 0
    close(3)                                = 0
    open("/mnt/newlog3.txt", O_RDWR|O_CREAT|O_APPEND, 0666) = 3   ##打开/mnt/newlog3.txt文件:对应脚本第7行
    fstat(3, {st_mode=S_IFREG|0644, st_size=438, ...}) = 0
    stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=414, ...}) = 0
    fstat(3, {st_mode=S_IFREG|0644, st_size=438, ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f67ad9e8000
    write(3, "2019-02-11 18:41:16 Hello world!"..., 46) = 46    ##写/mnt/newlog3.txt文件:对应脚本第10行
    close(3)                                = 0                                        ##关闭/mnt/newlog3.txt文件:对应脚本第11行
    munmap(0x7f67ad9e8000, 4096)            = 0
    rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f67ad2ac850}, {0x7f67ad54ed38, [], SA_RESTORER, 0x7f67ad2ac850}, 8) = 0
    exit_group(0)                           = ?

       从上面分析,shell脚本是边解析边执行,在执行完脚本后,再关闭脚本文件;perl和python脚本是先解析再执行,在执行脚本之前,

    已经完成了脚本的解析,直接关闭了脚本文件。

       如何控制perl或python脚本在执行脚本时不关闭脚本文件呢?我们需要从perl和python源码着手分析。

    perl和python源码分析

    python源码分析执行过程

    注:以python-2.7.9源码为分析蓝本

     1 /* Main program */
     2 
     3 int
     4 Py_Main(int argc, char **argv)
     5 {
     6 ......
     7         if (sts==-1) {
     8             /* call pending calls like signal handlers (SIGINT) */
     9             if (Py_MakePendingCalls() == -1) {
    10                 PyErr_Print();
    11                 sts = 1;
    12             } else {
    13                 sts = PyRun_AnyFileExFlags(
    14                     fp,
    15                     filename == NULL ? "<stdin>" : filename,
    16                     filename != NULL, &cf) != 0;  /* 是否执行python脚本文件作为是否关闭脚本文件的依据 */
    17             }
    18         }
    19 ......
    20 }
     1 /* Parse input from a file and execute it */
     2 
     3 int
     4 PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
     5                      PyCompilerFlags *flags)
     6 {
     7     if (filename == NULL)
     8         filename = "???";
     9     if (Py_FdIsInteractive(fp, filename)) { /* 交互式执行python命令 */
    10         int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
    11         if (closeit)
    12             fclose(fp);
    13         return err;
    14     }
    15     else
    16         return PyRun_SimpleFileExFlags(fp, filename, closeit, flags);  /* 执行python脚本文件 */
    17 }
    PyRun_SimpleFileExFlags调用PyRun_FileExFlags函数:
     1 PyObject *
     2 PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
     3                   PyObject *locals, int closeit, PyCompilerFlags *flags)
     4 {
     5     PyObject *ret;
     6     mod_ty mod;
     7     PyArena *arena = PyArena_New();
     8     if (arena == NULL)
     9         return NULL;
    10     /* Author: 解析python脚本文件 */
    11     mod = PyParser_ASTFromFile(fp, filename, start, 0, 0,
    12                                flags, NULL, arena);
    13     if (closeit)
    14         fclose(fp);  /* 解析完成后,关闭打开的python脚本文件 */
    15     if (mod == NULL) {
    16         PyArena_Free(arena);
    17         return NULL;
    18     }
    19     ret = run_mod(mod, filename, globals, locals, flags, arena); /* 执行python脚本 */
    20     PyArena_Free(arena);
    21     return ret;
    22 }

      从python脚本的执行过程看,当python命令传入的是脚本文件,则先解析python脚本内容,然后关闭打开的脚本文件,最后执行python脚本。

    perl源码分析执行过程

    注:以perl-5.16.3源码为分析蓝本

     1 #ifdef NO_ENV_ARRAY_IN_MAIN
     2 extern char **environ;
     3 int
     4 main(int argc, char **argv)   /* in miniperlmain.c */
     5 #else
     6 int
     7 main(int argc, char **argv, char **env)
     8 #endif
     9 {
    10 ......
    11     exitstatus = perl_parse(my_perl, xs_init, argc, argv, (char **)NULL); /* 做perl脚本解析: S_parse_body函数 */
    12     if (!exitstatus) {
    13     perl_run(my_perl); /* 执行perl脚本 */
    14     }    
    15 ......
    16 }
    17 
    18 
    19 #define parse_body(a,b)        S_parse_body(aTHX_ a,b)
    20  /* in perl.c */
    21 int
    22 perl_parse(pTHXx_ XSINIT_t xsinit, int argc, char **argv, char **env)
    23 {
    24 ......
    25 switch (ret) {
    26     case 0:
    27     parse_body(env,xsinit);  /* 真正做perl脚本解析的函数是S_parse_body  */
    28   ......
    29     break;
    30 ......
    31 }
     1 STATIC void *
     2 S_parse_body(pTHX_ char **env, XSINIT_t xsinit)
     3 {
     4   ......
     5     rsfp = open_script(scriptname, dosearch, &suidscript); /* 打开perl脚本文件 */
     6         if (!rsfp) {
     7             rsfp = PerlIO_stdin();
     8             lex_start_flags = LEX_DONT_CLOSE_RSFP;
     9         }
    10 
    11     ......
    12    lex_start(linestr_sv, rsfp, lex_start_flags); /* 准备perl解析环境和执行环境,实际函数是 Perl_lex_start */
    13      if(linestr_sv)
    14          SvREFCNT_dec(linestr_sv);
    15   ......
    16    /* now parse the script */
    17      SETERRNO(0,SS_NORMAL);
    18      /* yyparse函数解析perl脚本文件,然后关闭脚本: 实际调用Perl_lex_next_chunk函数关闭脚本文件 */
    19      if (yyparse(GRAMPROG) || PL_parser->error_count) { 
    20          if (PL_minus_c)
    21              Perl_croak(aTHX_ "%s had compilation errors.
    ", PL_origfilename);
    22          else {
    23              Perl_croak(aTHX_ "Execution of %s aborted due to compilation errors.
    ",
    24                  PL_origfilename);
    25          }
    26      }
    27  ......
    28 }

      从perl脚本的执行过程看,当perl命令传入的是脚本文件,则先解析perl脚本内容,然后关闭打开的脚本文件,最后执行perl脚本。 

      从python和perl源码分析过程看,两种脚本的执行过程都有一个参数控制着脚本的关闭方式。在python源码中,是PyRun_FileExFlags函数的closeit参数,

    当closeit为0是,python脚本执行完成后,不直接关闭打开的python脚本文件。在perl源码中,是Perl_lex_start函数的flags参数,当flags参数包含LEX_DONT_CLOSE_RSFP时,

    perl脚本解析完成后不直接关闭打开的perl脚本文件。

    源码修改

      依据我们的分析,可以修改源码控制python和perl脚本的关闭方式。源码修改方法如下。

    python源码修改方式:

     1 int 
     2 Py_Main(int argc, char **argv)
     3 {
     4 ......
     5     int closeit = 0, RunAnyFileExFlag = 0; /* 定义是否关闭python脚本的变量 */
     6 ......
     7     if (sts==-1) {
     8             /* call pending calls like signal handlers (SIGINT) */
     9             if (Py_MakePendingCalls() == -1) {
    10                 fprintf(stderr, "call Py_MakePendingCalls return -1...
    ");
    11                 PyErr_Print();
    12                 sts = 1;
    13             } else {
    14                 /* closeit decide whether closing python file */
    15                 closeit = 0;
    16                 RunAnyFileExFlag = 1;
    17                 sts = PyRun_AnyFileExFlags(
    18                     fp,
    19                     filename == NULL ? "<stdin>" : filename,
    20                     closeit /* filename != NULL */, &cf) != 0;  /* 如果是执行的脚本文件,则不关闭python脚本文件 */
    21             }
    22         }
    23 ......
    24     if(RunAnyFileExFlag && !closeit){  /* 在python结束之前关闭已经打开的python脚本文件 */
    25         fclose(fp);
    26     }
    27     #endif
    28     Py_Finalize();
    29 ......
    30 }

    perl源码修改方式:

    1 STATIC void * S_parse_body(pTHX_ char **env, XSINIT_t xsinit)
    2 /* lex_start中, 添加  LEX_DONT_CLOSE_RSFP 标记 */
    3 lex_start(linestr_sv, rsfp, lex_start_flags | LEX_DONT_CLOSE_RSFP);
    4     /* fprintf(stderr,"lex_start end......
    "); */
    5     if(linestr_sv)
    6                 SvREFCNT_dec(linestr_sv);

    笔者注: 在perl脚本执行完成后,由PerlIO_destruct函数完成后perl脚本文件的关闭动作!

    修改源码后测试结果

    perl脚本执行过程:

     1 # strace /root/liangjs/hello.pl    
     2 execve("/root/liangjs/hello.pl", ["/root/liangjs/hello.pl"], [/* 34 vars */]) = 0
     3 ....
     4 open("/root/liangjs/hello.pl", O_RDONLY) = 3    ##打开测试perl脚本文件
     5 ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffe347e36a0) = -1 ENOTTY (Inappropriate ioctl for device)
     6 lseek(3, 0, SEEK_CUR)                   = 0
     7 fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
     8 ......
     9 read(3, "#!/usr/bin/env perl
    
    $datestring"..., 8192) = 194  ##读取perl脚本文件内容并解析
    10 read(3, "", 8192)                       = 0     ##没有关闭perl脚本文件
    11 ......
    12 open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 4   ##获取时间信息
    13 ....
    14 open("/mnt/newlog3.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 4
    15 lseek(4, 0, SEEK_END)                   = 539
    16 ioctl(4, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffe347e3540) = -1 ENOTTY (Inappropriate ioctl for device)
    17 lseek(4, 0, SEEK_CUR)                   = 539
    18 fstat(4, {st_mode=S_IFREG|0644, st_size=539, ...}) = 0
    19 fcntl(4, F_SETFD, FD_CLOEXEC)           = 0
    20 brk(0)                                  = 0xf88000
    21 brk(0xfaa000)                           = 0xfaa000
    22 write(4, "Wed Feb 20 20:05:18 2019 hello w"..., 49) = 49  ##写日志文件/mnt/newlog3.txt
    23 close(4)                                = 0               ##关闭日志文件/mnt/newlog3.txt
    24 ........
    25 write(2, "call PerlIO_destruct......
    ", 27call PerlIO_destruct......
    26 ) = 27
    27 close(3)                                = 0       ##关闭已经打开的perl脚本文件
    28 exit_group(0)                           = ?
    29 +++ exited with 0 +++

    python脚本执行过程:

     1 stat("/root/liangjs/py_hello.py", {st_mode=S_IFREG|0755, st_size=958, ...}) = 0
     2 open("/root/liangjs/py_hello.py", O_RDONLY) = 3
     3 fstat(3, {st_mode=S_IFREG|0755, st_size=958, ...}) = 0
     4 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8830aa9000
     5 fstat(3, {st_mode=S_IFREG|0755, st_size=958, ...}) = 0
     6 lseek(3, 0, SEEK_SET)                   = 0
     7 read(3, "#!/usr/bin/env python
    
    import os"..., 936) = 936
     8 read(3, "t/newlog3.txt", 0644)
    ", 4096) = 22
     9 close(3)                                = 0   ##第一次打开python脚本文件后关闭!!!
    10 munmap(0x7f8830aa9000, 4096)            = 0
    11 stat("/root/liangjs/py_hello.py", {st_mode=S_IFREG|0755, st_size=958, ...}) = 0
    12 write(2, "second open python script file.."..., 34second open python script file...
    13 ) = 34
    14 open("/root/liangjs/py_hello.py", O_RDONLY) = 3
    15 fstat(3, {st_mode=S_IFREG|0755, st_size=958, ...}) = 0
    16 write(2, "call PyRun_AnyFileExFlags...
    ", 29call PyRun_AnyFileExFlags...
    17 ) = 29
    18 ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffd9da9be30) = -1 ENOTTY (Inappropriate ioctl for device)
    19 brk(0)                                  = 0x1a9e000
    20 brk(0x1ac0000)                          = 0x1ac0000
    21 fstat(3, {st_mode=S_IFREG|0755, st_size=958, ...}) = 0
    22 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8830aa9000
    23 read(3, "#!/usr/bin/env python
    
    import os"..., 4096) = 958
    24 stat("/etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned", 0x7ffd9da9bb10) = -1 ENOENT (No such file or directory)
    25 read(3, "", 4096)                       = 0   ##第二次打开python脚本文件后没有关闭动作!!!
    26 .....
    27 write(2, "close python file, RunAnyFileExF"..., 53close python file, RunAnyFileExFlag = 1, closeit = 0
    28 ) = 53
    29 close(3)                                = 0   ##最后关闭打开的python脚本文件
    30 munmap(0x7f8830aa9000, 4096)            = 0
    31 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f8830743370}, {0x4fe640, [], SA_RESTORER, 0x7f8830743370}, 8) = 0
    32 exit_group(0)                           = ?
    33 +++ exited with 0 +++

      修改python和perl源码后,从以上执行过程来看,都是在执行完python和perl脚本之后才关闭相应的脚本文件。

    说明这种修改方式符合我们控制perl和python脚本文件关闭时刻的预期!

    进一步的工作

      默认动作还是解析完perl和python脚本后,直接关闭脚本文件;可以添加脚本执行参数,控制脚本文件解析完成后是否关闭。

    相关议题

           在perl较低版本中,例如perl-5.10.0版本,yy_parser结构体中没有lex_flags成员,所以要实现控制脚本文件关闭,需要添加相应的成员和控制流程。

    Perl-5.10.0版本源码修改

    parser.h文件

     1 typedef struct yy_parser {
     2 
     3     /* parser state */
     4 
     5     struct yy_parser *old_parser; /* previous value of PL_parser */
     6 ......
     7     char    tokenbuf[256];
     8     /* 2018-11-14, with no close file */
     9     U8     lex_flags;
    10 } yy_parser;

    toke.c文件:

     1 void
     2 Perl_lex_start(pTHX_ SV *line, PerlIO *rsfp, bool new_filter)
     3 {
     4 ......
     5 parser->copline = NOLINE;
     6     parser->lex_state = LEX_NORMAL;
     7     parser->expect = XSTATE;
     8     parser->rsfp = rsfp;
     9     parser->rsfp_filters = (new_filter || !oparser) ? newAV()
    10         : (AV*)SvREFCNT_inc(oparser->rsfp_filters);
    11     /* add 2018-11-14, for no closefile */
    12     parser->lex_flags = LEX_DONT_CLOSE_RSFP;
    13 
    14     Newx(parser->lex_brackstack, 120, char);
    15     Newx(parser->lex_casestack, 12, char);
    16     *parser->lex_casestack = '';
    17 ......
    18 }
     1 /* delete a parser object */
     2 
     3 void
     4 Perl_parser_free(pTHX_  const yy_parser *parser)
     5 {
     6     PL_curcop = parser->saved_curcop;
     7     SvREFCNT_dec(parser->linestr);
     8 
     9     /* modi 2018-11-14, with no close file */
    10     if ( parser->rsfp == PerlIO_stdin() || (parser->lex_flags & LEX_DONT_CLOSE_RSFP) )
    11         PerlIO_clearerr(parser->rsfp);
    12     else if (parser->rsfp && parser->old_parser
    13               && parser->rsfp != parser->old_parser->rsfp)
    14         PerlIO_close(parser->rsfp);
    15     SvREFCNT_dec(parser->rsfp_filters);
    16 
    17     Safefree(parser->stack);
    18     Safefree(parser->lex_brackstack);
    19     Safefree(parser->lex_casestack);
    20     PL_parser = parser->old_parser;
    21     Safefree(parser);
    22 }
     1 STATIC char *
     2 S_skipspace(pTHX_ register char *s)
     3 {
     4 ......
     5 
     6         if (PL_preprocess && !PL_in_eval)
     7         (void)PerlProc_pclose(PL_rsfp);
     8         else if ((PerlIO*)PL_rsfp == PerlIO_stdin() || (PL_parser->lex_flags & LEX_DONT_CLOSE_RSFP) )
     9         /* modi 2018-11-14, with no close file */
    10         PerlIO_clearerr(PL_rsfp);
    11         else
    12         (void)PerlIO_close(PL_rsfp);
    13         PL_rsfp = NULL;
    14         return s;
    15     }
    16 
    17     /* not at end of file, so we only read another line */
    18     /* make corresponding updates to old pointers, for yyerror() */
    19     oldprevlen = PL_oldbufptr - PL_bufend
    20 ......
    21 }
    22 
    23 }
     1 int
     2 Perl_yylex(pTHX)
     3 {
     4 ......
     5 do {
     6         bof = PL_rsfp ? TRUE : FALSE;
     7         if ((s = filter_gets(PL_linestr, PL_rsfp, 0)) == NULL) {
     8           fake_eof:
     9 #ifdef PERL_MAD
    10         PL_realtokenstart = -1;
    11 #endif
    12         if (PL_rsfp) {
    13             if (PL_preprocess && !PL_in_eval)
    14             (void)PerlProc_pclose(PL_rsfp);
    15             else if ((PerlIO *)PL_rsfp == PerlIO_stdin() || (PL_parser->lex_flags & LEX_DONT_CLOSE_RSFP) )
    16             /* modi 2018-11-14, with no close */
    17             PerlIO_clearerr(PL_rsfp);
    18             else
    19             (void)PerlIO_close(PL_rsfp);
    20             PL_rsfp = NULL;
    21             PL_doextract = FALSE;
    22         }
    23 ......
    24 
    25 }

    PS:您的支持是对博主最大的鼓励,感谢您的认真阅读。
            本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    06.数组模拟栈-简易计算器
    05.单向环形链表应用 -- 约瑟夫环
    02.数组模拟环形队列
    01.稀疏矩阵与二维数组相互转化
    大数字相乘
    13.打包发布
    悦苗园公益活动
    程序猿打招自己的电子图书馆
    【技能】提高网站可用性
    【微信技能】如何通过微信号知道对方微信的二维码
  • 原文地址:https://www.cnblogs.com/smith9527/p/10350214.html
Copyright © 2020-2023  润新知