Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux:为什么从Python3(或其他解释器)将/bin/bash作为子进程启动会使父进程不受SIGINT(ctrl-c)的影响?_Python_Bash_Subprocess_Signals_Posix - Fatal编程技术网

Linux:为什么从Python3(或其他解释器)将/bin/bash作为子进程启动会使父进程不受SIGINT(ctrl-c)的影响?

Linux:为什么从Python3(或其他解释器)将/bin/bash作为子进程启动会使父进程不受SIGINT(ctrl-c)的影响?,python,bash,subprocess,signals,posix,Python,Bash,Subprocess,Signals,Posix,有人能解释一下这里到底发生了什么吗?为什么从Python3、Rake、Ruby或Make作为子进程启动shell(bash、ksh)会导致终端表现得像父进程不接收由ctrl-c生成的SIGINT一样?当子进程不是shell时,父进程当然会中断,那么到底发生了什么?我看到在整个执行过程中,子进程组和父进程组都是同一进程组的一部分 孩子如何使父母对SIGINT免疫?如果这是通过通道重新路由或其他一些花哨的伎俩实现的,请通过一个示例说明如何实现。我想修复我所看到的具有CLI的EDA工具之间的不一致行为

有人能解释一下这里到底发生了什么吗?为什么从Python3、Rake、Ruby或Make作为子进程启动shell(bash、ksh)会导致终端表现得像父进程不接收由ctrl-c生成的SIGINT一样?当子进程不是shell时,父进程当然会中断,那么到底发生了什么?我看到在整个执行过程中,子进程组和父进程组都是同一进程组的一部分

孩子如何使父母对SIGINT免疫?如果这是通过通道重新路由或其他一些花哨的伎俩实现的,请通过一个示例说明如何实现。我想修复我所看到的具有CLI的EDA工具之间的不一致行为;有些操作与Bash在子程序中的操作相同,而另一些则作为孤儿

测试代码: 这段代码将首先启动bash作为一个子进程,尽管它被Python3包装,但不能被ctrl-c中断;您必须退出才能返回到父级。然后,代码将启动child.py,它有一个中断处理程序;当在子级中按住ctrl-c键时,您将看到parent.py raise和child.py同时中断,使child.py处于孤立状态,而其SIGINT处理程序向stdout发出指令;退出状态为父级;孩子的身份丢失了

parent.py:

#!/usr/bin/env python3
import os, sys
import subprocess
import signal
import time

PID = os.getpid()
PGID = os.getpgid(PID)
PROC_LABEL = f'PARENT: pid={PID}, pgid={PGID}'

print(f"{PROC_LABEL}: spawning bash...")
proc = subprocess.Popen(['bash'])
ret = proc.wait()
print(f"{PROC_LABEL}: child exit status:", proc.returncode)

print(f"{PROC_LABEL}: spawning ./child.py...")
proc = subprocess.Popen(['./child.py'])
proc.wait()
print(f"{PROC_LABEL}: child exit status:", proc.returncode)
sys.exit(0)
child.py

#!/usr/bin/env python3
import os, sys
import signal
import time

PID = os.getpid()
PGID = os.getpgid(PID)
PROC_LABEL = f"CHILD : pid={PID}; pgid={PGID}"

def intr_handler(sig, frame):
  print(f'\n{PROC_LABEL}: Trapped: {signal.Signals(sig).name}')
  for idx in range(3,0,-1):
    print(f"{PROC_LABEL}: sleeping for {idx} seconds")
    time.sleep(1)
  print(f"{PROC_LABEL}: bye")
  sys.exit(100)
signal.signal(signal.SIGINT, intr_handler)

ret = input(f"{PROC_LABEL}: type something: ")
print("input:", ret)
sys.exit(0)
执行:

$ ./parent.py 
PARENT: pid=3121412, pgid=3121412: spawning bash...
bash> ^C
bash> exit 0
exit
PARENT: pid=3121412, pgid=3121412: child exit status: 0
PARENT: pid=3121412, pgid=3121412: spawning ./child.py...
CHILD : pid=3121728; pgid=3121412: type something: ^C
CHILD : pid=3121728; pgid=3121412: Trapped: SIGINT
CHILD : pid=3121728; pgid=3121412: sleeping for 3 seconds
Traceback (most recent call last):
  File "./parent.py", line 18, in <module>
    proc.wait()
  File "/m/tools/lang/python/pyenv/versions/3.7.6/lib/python3.7/subprocess.py", line 1019, in wait
    return self._wait(timeout=timeout)
  File "/m/tools/lang/python/pyenv/versions/3.7.6/lib/python3.7/subprocess.py", line 1653, in _wait
    (pid, sts) = self._try_wait(0)
  File "/m/tools/lang/python/pyenv/versions/3.7.6/lib/python3.7/subprocess.py", line 1611, in _try_wait
    (pid, sts) = os.waitpid(self.pid, wait_flags)
KeyboardInterrupt
$ CHILD : pid=3121728; pgid=3121412: sleeping for 2 seconds
CHILD : pid=3121728; pgid=3121412: sleeping for 1 seconds
CHILD : pid=3121728; pgid=3121412: bye
echo $?
1
$./parent.py
父项:pid=312412,pgid=312412:生成bash。。。
bash>^C
bash>退出0
出口
父项:pid=312412,pgid=312412:子项退出状态:0
父项:pid=312412,pgid=312412:繁殖。/child.py。。。
子项:pid=3121728;pgid=312412:键入某些内容:^C
子项:pid=3121728;pgid=312412:陷阱:SIGINT
子项:pid=3121728;pgid=312412:睡眠3秒
回溯(最近一次呼叫最后一次):
文件“/parent.py”,第18行,在
进程等待()
文件“/m/tools/lang/python/pyenv/versions/3.7.6/lib/python3.7/subprocess.py”,第1019行,正在等待
返回自我。\u等待(超时=超时)
文件“/m/tools/lang/python/pyenv/versions/3.7.6/lib/python3.7/subprocess.py”,第1653行,在等待中
(pid,sts)=self.\u try\u wait(0)
文件“/m/tools/lang/python/pyenv/versions/3.7.6/lib/python3.7/subprocess.py”,第1611行,在“try\u wait”中
(pid,sts)=os.waitpid(self.pid,wait_标志)
键盘中断
$CHILD:pid=3121728;pgid=312412:睡眠2秒
子项:pid=3121728;pgid=312412:睡眠1秒
子项:pid=3121728;pgid=312412:再见
回声$?
1.
在与您的进程不同的进程组中运行,并作为前台进程接管,使其接收信号,而父进程不接收信号

“诸如
bash(1)
之类的程序使用
setpgid()
getpgrp()
调用来创建进程组,以实现shell作业控制。”

您可以使用
ps o pid,pgrp
检查流程组。对于常规子进程,您将看到脚本和子进程的相同进程组,而一些程序(如)创建新的进程组

还安装自己的信号处理程序

Linux上的示例:

root# grep ^Sig /proc/$SUBPROCESS_PID/status
SigPnd: 0000000000000000                     # pending
SigBlk: 0000000000000000                     # blocked
SigIgn: 0000000000380004                     # ignored
SigCgt: 000000004b817efb                     # caught
SigCgt
字段是有趣的。这是一个位掩码:

$ bc <<< "ibase=16; obase=2; 4B817EFB"
1001011100000010111111011111011
                             |
                            SIGINT

如果现在将此程序放在
/parent.py
脚本中,您将看到类似的行为。

非常感谢您的详细回答。起初我以为它们一定在不同的进程组中,因为我对信号的理解,但在查看python和bash进程时,我把ps命令搞砸了;我要求的是“gid”,而不是“pgid”。谢谢@user3286792很好,它很有帮助!不客气!
// sig.cpp

#include <unistd.h>

#include <cerrno>
#include <csignal>
#include <cstring>
#include <iostream>
#include <stdexcept>

static int gsig = -1; // the latest caught signal

static void sighandler(int sig) {
    gsig = sig;
}

int check(int val) {
    if(val) std::runtime_error(std::strerror(errno));
    return val;
}

int main() {
    try {
        // catch a lot...
        for(auto sig : {SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM}) {
            check(std::signal(sig, sighandler)==SIG_ERR);
        }

        /* ignore terminal settings changes */
        check(signal(SIGTTOU, SIG_IGN)==SIG_ERR);

        // create new process group
        check(::setpgrp());

        // get the created process group
        pid_t pgrp = ::getpgrp();

        // set forground process group to the created process group
        check(::tcsetpgrp(::fileno(stdin), pgrp));

        std::cout << "-- waiting --" << std::endl;

        while(true) {
            ::pause();
            std::cout << "got signal " << gsig << std::endl;
        }
    } catch(const std::exception& ex) {
        std::cerr << "Exception: " << ex.what() << std::endl;
    }
}
$ g++ -o sig sig.cpp -std=c++11 -O3