Python踩坑之旅其二裸用os.system的原罪


代碼示例支持
平臺: Centos 6.3
Python: 2.7.14
Github: https://github.com/baidu/CUP

1.1 踩坑案例

今天的坑不只包括裸用os.system還包括裸用相關的家族:python

  • os.popen
    • subprocess家族
      • subprocess.call
      • subprocess.Popen
      • subprocess.run
    • commands家族 (py2.6後已不推薦使用, depreciated. Py3刪除)
      • commands.getstatusoutput

這些坑是新同窗很是容易踩,並且 code review 過程當中容易漏掉:linux

[1] 長期運行 Service 中裸用以函數家族git

  • 裸用以上 shell 執行家族可能出現script 執行 hang 住進而 hang 住邏輯執行線程,長時間積累進而佔滿全部執行線程而服務宕機狀況
  • 大內存消耗 service fork 子進程直接執行 script
    • 若是該 script hang 住
    • 而且原進程內存進行頻繁修改(或者堆積修改, 雖然有 Copy-On-Write技術),但因爲內存巨大,依然有內存風險

[2] 自動化測試中裸用以上函數家族而不加以保護github

  • 單個 case 若是執行 script 腳本 hang 住會致使 hang 掉整個case 集
  • 不設計 case 超時機制致使case 集合運行時間不可控

1.2 填坑解法

  1. 支持超時 kill 策略,禁止任何狀況下的 shell 執行裸用家族函數

提供一個做者的代碼參考: https://github.com/baidu/CUP/blob/master/cup/shell/oper.pyshell

from cup import shell
        shellexec = shell.ShellExec()
        # timeout=None will block the execution until it finishes
        shellexec.run('/bin/ls', timeout=None)
        # timeout>=0 will open non-blocking mode
        # The process will be killed if the cmd timeouts
        shellexec.run(cmd='/bin/ls', timeout=100)

見ShellExec類的run函數centos

  1. 內存消耗型服務/進程, 長期運行服務進程避免fork 進程執行 shell 命令

1.3 坑位分析

建議看下第二章節關於進程和子進程繼承類信息,script使用上述家族進行執行時,採用了啓動一個子進程的方式bash

1.4.1 技術關鍵字

  • os.system家族
  • subprocess家族

1.5 填坑總結

Shell執行是個很是常見的操做,因此不少同窗特別是新同窗,在使用過程當中常常不注意而隨意使用。 裸用一時爽,進程死亡火葬場app

2. 前坑回顧

2.1 Linux中, 子進程拷貝父進程哪些信息

  • 先說與父進程不一樣的
    • pid, ppid
    • memory locks
    • tms_utime、tms_stime、tms_cutime、tms_ustime
    • pending signals
    • semaphore adjustments
    • file lock
    • pending alarms

參考資料來源:async

  • Linux Programmer's Manual ( man fork )
    • CentOS release 6.3 (Final)
    • Linux Kernel 2.6.32
fork()  creates a new process by duplicating the calling process.  The new process, referred to as the child, is an exact duplicate of the calling process, referred to as the parent, except for the follow-
ing points:

    *  The child has its own unique process ID, and this PID does not match the ID of any existing process group (setpgid(2)).

    *  The child's parent process ID is the same as the parent's process ID.

    *  The child does not inherit its parent's memory locks (mlock(2), mlockall(2)).

    *  Process resource utilizations (getrusage(2)) and CPU time counters (times(2)) are reset to zero in the child.

    *  The child's set of pending signals is initially empty (sigpending(2)).

    *  The child does not inherit semaphore adjustments from its parent (semop(2)).

    *  The child does not inherit record locks from its parent (fcntl(2)).

    *  The child does not inherit timers from its parent (setitimer(2), alarm(2), timer_create(2)).

    *  The child does not inherit outstanding asynchronous I/O operations from its parent (aio_read(3), aio_write(3)), nor does it inherit any asynchronous I/O contexts from its parent (seeio_setup(2)).

       The process attributes in the preceding list are all specified in POSIX.1-2001.  The parent and child also differ with respect to the following Linux-specific process attributes:

    *  The child does not inherit directory change notifications (dnotify) from its parent (see the description of F_NOTIFY in fcntl(2)).

    *  The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does not receive a signal when its parent terminates.

    *  Memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag are not inherited across a fork().

    *  The termination signal of the child is always SIGCHLD (see clone(2)).

在說繼承、拷貝父進程的

  • 包括
    • 內部數據空間
    • 堆棧
    • 用戶 ID、組 ID、eid 有效用戶 id、有效組 id、用戶 id 標誌和設置組 id 標誌
    • 進程組 id
    • 會話 id
    • 終端
    • 當前目錄、根目錄
    • 文件模式屏蔽字
    • 信號屏蔽設置
    • 打開文件描述符
    • 環境
    • 共享存儲段
    • 存儲映射
    • 資源限制

此外

2.2 Agent常駐進程選擇>60000端口的意義

在 Linux 系統中, 通常系統會自動替程序選擇端口鏈接到用戶指定的目的端口, 而這個端口範圍是提早設定好的, 好比做者的 centos:

$ cat /proc/sys/net/ipv4/ip_local_port_range
10000   60000
相關文章
相關標籤/搜索