s
현재 디렉토리에 다음과 같은 Bash 스크립트가 있습니다.
#!/bin/bash
pipe_test() {
( set -m; (
$1
); set +m ) |
(
$2
)
}
pipe_test "$1" "$2"
예를 들어 내가 전화하면
./s yes less
스크립트가 중지되었습니다. ( less
IE 및 IE 대신 시도한 다른 호출기를 사용하는 경우에도 more
비슷한 일이 발생합니다 most
.) 그러나 fg
내장된 기능을 통해 계속할 수 있습니다.
set -m
서브쉘의 프로세스에 다른 프로세스 그룹 ID가 부여되도록 서브쉘에 작업 제어(활성화)가 있기를 원합니다 .
내 시스템에 대한 정보:
$ bashbug
...
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -g -O2 -fdebug-prefix-map=/build/bash-cP61jF/bash-5.0=. -fstack-protector-strong -Wformat -Werror=format->
uname output: Linux jarnos-OptiPlex-745 5.4.0-29-generic #33-Ubuntu SMP Wed Apr 29 14:32:27 UTC 2020 x86_64 x86_64 x86_64 GNU>
Machine Type: x86_64-pc-linux-gnu
Bash Version: 5.0
Patch Level: 16
Release Status: release
$ less --version
less version: 551
답변1
이런 일이 발생하는 이유는 작업 제어( set -m
)를 활성화하면 프로세스 그룹화뿐만 아니라 "포그라운드" 및 "백그라운드" 작업을 처리하기 위한 메커니즘도 제공되기 때문입니다. 이 "메커니즘"은 작업 제어가 활성화되면 순서대로 실행되는 모든 명령이 포그라운드 프로세스 그룹이 된다는 것을 의미합니다.
즉, 해당 하위 쉘(파이프라인의 왼쪽 부분)이 작업 제어를 활성화하면 실제로 해당 시점까지 터미널을 소유하고 있던 전체 파이프라인에서 터미널을 훔치고 프로세스는 예제에 포함됩니다 less
. 배경이 되어 더 이상 터미널을 사용할 수 없게 됩니다. 그러면 less
계속해서 터미널에 접속을 하게 되기 때문에 중지되게 됩니다 .
발행을 통해 fg
터미널을 전체 파이프라인으로 반환하므로 반환 시 less
모든 것이 끝납니다. 각 추가 명령은 작업 제어 하위 쉘에서 실행하지 않는 한 터미널을 다시 훔칩니다.
이 문제를 해결하는 한 가지 방법은 간단히 백그라운드에서 작업 제어 하위 셸을 실행하는 것입니다.
( set -m; (
$1
) & set +m ) |
(
$2
)
필요에 따라 서로 다른 프로세스 그룹에서 run으로 표시되는 명령을 사용하게 되며 $1
, 백그라운드 모드는 터미널을 도용하여 터미널을 파이프에 남겨 두는 것을 방지합니다 $2
.
물론 이렇게 $1
하려면 in 명령이 터미널 자체를 읽지 않아야 합니다. 그렇지 않으면 터미널 자체를 읽으려고 시도하자마자 중지됩니다.
또한 위에서 말한 것과 유사하게 추가하려는 다른 작업 제어 하위 쉘은 추가할 때까지 동일한 "백그라운드" 처리가 필요합니다. set +m
그렇지 않으면 각 추가 작업 제어 하위 쉘이 다시 생성됩니다. 단말기.
즉, 프로세스 종료만을 목적으로 프로세스 그룹화가 필요한 경우 프로세스 그룹화를 사용하여 pkill
해당 프로세스를 대상으로 삼는 것을 고려할 수 있습니다. 예를 들어, pkill -P
신호가 해당 프로세스로 전송됩니다.부모표시된 PID입니다. 이렇게 하면 자녀의 PID를 알면 자녀의 모든 자녀(손자는 제외)를 대상으로 지정할 수 있습니다.
답변2
이를 제거하면 set -m
문제가 해결됩니다(어쨌든 어떻게 해야 할까요?).
커널은 다음과 같은 방법으로 세 가지 프로세스를 중지합니다 SIGTTOU
.
- 스크립트 프로세스
- 서브쉘
less
하지만 yes
. 해당 프로세스는 아마도 에 의해 별도의 프로세스 그룹에 배치됩니다 set -m
. 따라서 커널은 해당 파이프의 모든 프로세스에 액세스하려고 시도하지만 하나를 놓칩니다. 그러나 이러한 부재가 "중지됨" 메시지의 원인은 아닙니다.
일반적으로 SIGTTOU
터미널에 쓰기를 시도하는 백그라운드 프로세스로 인해 발생합니다. 하지만 이것이 유일한 이유는 아닙니다.
int SIGTTOU
SIGTTIN과 유사하지만 백그라운드 작업의 프로세스가 터미널에 쓰거나 해당 모드를 설정하려고 할 때 생성됩니다. 다시 말하지만, 기본 작업은 프로세스를 중지하는 것입니다. TOSTOP 출력 모드가 설정된 경우 SIGTTOU는 터미널에 쓰려고 할 때만 생성됩니다. 출력 모드를 참조하세요.
바라보다https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html
이전의 마지막 시스템 호출은 다음과 같습니다(통과 less
).
ioctl(3, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig -icanon -echo ...}) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
그래서 내 평가는 이상한 이유(예 set -m
:)로 인해 파이프가 배경에 배치되고 있다는 것입니다. 다음과 같은 여러 시스템 호출이 있습니다.
ioctl(255, TIOCSPGRP, [23715]
다양한 프로세스를 통해. 마지막은 서브 쉘을 통한 것입니다
ioctl(2, TIOCSPGRP, [23718]) = 0
yes
(다른 멤버 없이) 자체 프로세스 그룹의 리더로 만든 후 전경 프로세스 그룹으로 만듭니다.
setpgid(23718, 23718 <unfinished ...>