溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時(shí)間:2020-09-17 04:57:41 來(lái)源:網(wǎng)絡(luò) 閱讀:494 作者:mythmgn 欄目:編程語(yǔ)言

[TOC]

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

歡迎關(guān)注公眾號(hào)進(jìn)行技術(shù)互動(dòng)和討論:
Python踩坑之旅其二裸用os.system的原罪

1.1 踩坑案例

今天的坑不僅包括裸用os.system還包括裸用相關(guān)的家族:

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

這些坑是新同學(xué)非常容易踩,而且 code review 過(guò)程中容易漏掉:

[1] 長(zhǎng)期運(yùn)行 Service 中裸用以函數(shù)家族

  • 裸用以上 shell 執(zhí)行家族可能出現(xiàn)script 執(zhí)行 hang 住進(jìn)而 hang 住邏輯執(zhí)行線程,長(zhǎng)時(shí)間積累進(jìn)而占滿所有執(zhí)行線程而服務(wù)宕機(jī)情況
  • 大內(nèi)存消耗 service fork 子進(jìn)程直接執(zhí)行 script
    • 如果該 script hang 住
    • 并且原進(jìn)程內(nèi)存進(jìn)行頻繁修改(或者堆積修改, 雖然有 Copy-On-Write技術(shù)),但由于內(nèi)存巨大,依然有內(nèi)存風(fēng)險(xiǎn)

[2] 自動(dòng)化測(cè)試中裸用以上函數(shù)家族而不加以保護(hù)

  • 單個(gè) case 如果執(zhí)行 script 腳本 hang 住會(huì)導(dǎo)致 hang 掉整個(gè)case 集
  • 不設(shè)計(jì) case 超時(shí)機(jī)制導(dǎo)致case 集合運(yùn)行時(shí)間不可控

1.2 填坑解法

  1. 支持超時(shí) kill 策略,禁止任何情況下的 shell 執(zhí)行裸用家族函數(shù)

提供一個(gè)作者的代碼參考: https://github.com/baidu/CUP/blob/master/cup/shell/oper.py

        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)

見(jiàn)ShellExec類(lèi)的run函數(shù)

  1. 內(nèi)存消耗型服務(wù)/進(jìn)程, 長(zhǎng)期運(yùn)行服務(wù)進(jìn)程避免fork 進(jìn)程執(zhí)行 shell 命令

1.3 坑位分析

建議看下第二章節(jié)關(guān)于進(jìn)程和子進(jìn)程繼承類(lèi)信息,script使用上述家族進(jìn)行執(zhí)行時(shí),采用了啟動(dòng)一個(gè)子進(jìn)程的方式

1.4.1 技術(shù)關(guān)鍵字

  • os.system家族
  • subprocess家族

1.5 填坑總結(jié)

Shell執(zhí)行是個(gè)非常常見(jiàn)的操作,所以很多同學(xué)特別是新同學(xué),在使用過(guò)程中經(jīng)常不注意而隨意使用。 裸用一時(shí)爽,進(jìn)程死亡火葬場(chǎng)

2. 前坑回顧

2.1 Linux中, 子進(jìn)程拷貝父進(jìn)程哪些信息

  • 先說(shuō)與父進(jìn)程不同的
    • pid, ppid
    • memory locks
    • tms_utime、tms_stime、tms_cutime、tms_ustime
    • pending signals
    • semaphore adjustments
    • file lock
    • pending alarms

參考資料來(lái)源:

  • 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)).

在說(shuō)繼承、拷貝父進(jìn)程的

  • 包括
    • 內(nèi)部數(shù)據(jù)空間
    • 堆棧
    • 用戶 ID、組 ID、eid 有效用戶 id、有效組 id、用戶 id 標(biāo)志和設(shè)置組 id 標(biāo)志
    • 進(jìn)程組 id
    • 會(huì)話 id
    • 終端
    • 當(dāng)前目錄、根目錄
    • 文件模式屏蔽字
    • 信號(hào)屏蔽設(shè)置
    • 打開(kāi)文件描述符
    • 環(huán)境
    • 共享存儲(chǔ)段
    • 存儲(chǔ)映射
    • 資源限制

此外

  • 在父進(jìn)程創(chuàng)建 (fork) 出一個(gè)子進(jìn)程過(guò)程中, 為了加速, 會(huì)使用叫做 copy-on-write 的技術(shù).
    • 這項(xiàng)技術(shù)在存儲(chǔ)軟件領(lǐng)域也經(jīng)常使用
    • 附上一個(gè)關(guān)于它的討論,點(diǎn)擊查看

2.2 Agent常駐進(jìn)程選擇>60000端口的意義

在 Linux 系統(tǒng)中, 一般系統(tǒng)會(huì)自動(dòng)替程序選擇端口連接到用戶指定的目的端口, 而這個(gè)端口范圍是提前設(shè)定好的, 比如作者的 centos:

$ cat /proc/sys/net/ipv4/ip_local_port_range
10000   60000
  • 選擇 60000 以上的端口可以避免沖突
  • 附上一篇討論該ip_local_port_range的文章,歡迎查看

歡迎關(guān)注公眾號(hào)進(jìn)行技術(shù)互動(dòng)和討論:

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

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI