less가 내 스크립트를 중지합니다. 왜 이런 일이 발생하며 이를 방지하는 방법은 무엇입니까?

less가 내 스크립트를 중지합니다. 왜 이런 일이 발생하며 이를 방지하는 방법은 무엇입니까?

s현재 디렉토리에 다음과 같은 Bash 스크립트가 있습니다.

#!/bin/bash
pipe_test() {
    ( set -m; (
        $1
    ); set +m ) | 
    (
        $2
    )
}
pipe_test "$1" "$2"

예를 들어 내가 전화하면

./s yes less

스크립트가 중지되었습니다. ( lessIE 및 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 ...>

관련 정보