![쉘이 백그라운드와 포그라운드에서 두 내장 명령의 표준 입력을 어떻게 리디렉션하는지 명확히 합니다.](https://linux55.com/image/208640/%EC%89%98%EC%9D%B4%20%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EC%99%80%20%ED%8F%AC%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EC%97%90%EC%84%9C%20%EB%91%90%20%EB%82%B4%EC%9E%A5%20%EB%AA%85%EB%A0%B9%EC%9D%98%20%ED%91%9C%EC%A4%80%20%EC%9E%85%EB%A0%A5%EC%9D%84%20%EC%96%B4%EB%96%BB%EA%B2%8C%20%EB%A6%AC%EB%94%94%EB%A0%89%EC%85%98%ED%95%98%EB%8A%94%EC%A7%80%20%EB%AA%85%ED%99%95%ED%9E%88%20%ED%95%A9%EB%8B%88%EB%8B%A4..png)
아주 작은 질문이 있습니다. 이 명령을 예로 들어보겠습니다 cat < file.txt
.
쉘이 <를 보면 새 프로세스를 분기하여 stdin(0 파일 설명자)을 리디렉션하고 stdin을 해당 특정 프로세스 환경에 대한 해당 파일로만 변경합니다(내장되어 있지 않기 때문에).
그러나 내장 명령이 있는 경우 쉘은 새 프로세스를 포크하지 않고 단지 전체 터미널 환경에서 지정된 파일로 stdin을 변경합니다(전체 터미널 환경에서 작동하는지 여부는 알 수 없지만 쉘이 수행하기 때문에). 새 프로세스를 포크하지 않고 프로그램(내장)에 대해서만 수행하는 방법을 모르고 프로그램이 완료되면 다시 /dev/pts/0(일반 stdin)으로 변경합니다.
그러나 그것은 나에게 모호합니다. 두 개의 내장 명령(그 중 하나는 백그라운드에 있음)을 실행하고 둘 다 stdout 또는 stdin을 두 개의 다른 파일로 리디렉션하는 경우 둘 다 stdin(또는 stdout)을 자체 파일로 변경한다는 의미이지만 이는 다음 중 하나를 의미합니다. 프로그램은 포크하지 않기 때문에 두 표준 입력을 동시에 리디렉션할 수 없기 때문에 다른 프로그램의 표준 입력을 사용합니다.
이것은 단지 경우입니다. 위에서 언급한 내용이 사실이라면 stdin은 포크되지 않기 때문에 내장 프로그램에 대한 프로그램뿐만 아니라 전체 터미널 환경에 대해 변경됩니다.
명확하지 않으면 설명하기가 정말 어렵지만 다른 방식으로 설명하겠습니다. 내장 기능을 사용하면 쉘은 새 프로세스를 포크하지 않습니다. 하나는 bg에서 실행되고 다른 하나는 fg에서 실행됩니다. 둘 다 stdin을 리디렉션하거나 stdin을 다른 파일로 변환하는 경우 쉘 내장 프로그램이 프로그램 실행이 끝날 때까지 터미널 전체에서 stdin을 변경하므로 어떻게 동시에 다른 stdin을 가질 수 있습니까?
답변1
껍데기하다다양한 조건에서 내장 명령을 포크합니다. 조건 중 하나는 백그라운드 작업을 실행하는 것입니다. 여기서 print
및 는 zselect
모두 백그라운드 함수에 내장된 함수입니다 inbg
.
#!/usr/bin/env zsh
zmodload zsh/system
zmodload zsh/zselect
function inbg { print BG PID $sysparams[pid] > pid.bg ; zselect -t 333 }
function infg { print FG PID $sysparams[pid] > pid.fg }
inbg &
infg
wait
실행되면 inbg
함수는 다른 프로세스 ID로 실행됩니다. 이는 프로세스 트리를 확인하거나 하위 프로세스 ID에 대한 올바른 로깅( $sysparams[pid]
ZSH를 통해, 여기서는 셸이 다름)을 통해 관찰할 수 있습니다.
% zsh builtins & sleep 1; pgrep -lf builtins; wait
[1] 97170
97170 zsh builtins
97172 zsh builtins
[1] + done zsh builtins
% cat pid*
BG PID 97172
FG PID 97170
pid.bg
이를 통해 표준 출력을
pid.fg
한 프로세스에서 다른 프로세스로 쉽게 다시 연결할 수 있습니다.
그렇지 않으면 "전체 터미널 환경"은 어떤 식으로든 영향을 받지 않습니다. 쉘(또는 UNIX의 모든 프로세스)은 파일 설명자를 다시 연결하여 표준 출력의 출력을 한 대상에서 다른 대상으로 일시적으로 변경할 수 있습니다. 이를 통해 내장 함수가 잠시 동안 다른 곳으로 기준을 보낼 수 있습니다.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
int nullfd, savefd;
puts("out1");
savefd = dup(STDOUT_FILENO);
nullfd = open("/dev/null", O_WRONLY);
close(STDOUT_FILENO);
dup2(nullfd, STDOUT_FILENO);
puts("nothing");
close(nullfd);
close(STDOUT_FILENO);
dup2(savefd, STDOUT_FILENO);
puts("out2");
return 0;
}
위의 재배선을 사용하면 nothing
stdout으로 인쇄하는 /dev/null
사이에
puts
(하나 이상의 시스템 호출이 실패하지 않는 한 위 코드는 명확성을 확인하지 않습니다):
% make redirect-stdout
cc redirect-stdout.c -o redirect-stdout
% ./redirect-stdout
out1
out2
이러한 종류의 "재배선"에 대한 자세한 내용은 APUE(Advanced Programming in Unix Environments)를 참조 pipe(2)
하세요 dup(2)
.
프로세스 상태
두 개의 스레드를 생성하고 표준 출력을 두 개의 다른 파일로 리디렉션하여 표준 I/O의 리디렉션이 프로세스별임을 증명할 수 있습니다.
#include <err.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_cond_t th_cnd = PTHREAD_COND_INITIALIZER;
pthread_mutex_t th_mtx = PTHREAD_MUTEX_INITIALIZER;
void *threader(void *);
int main(void) {
pthread_t xxx, yyy;
pthread_create(&xxx, NULL, threader, (void *) "thread.x");
pthread_create(&yyy, NULL, threader, (void *) "thread.y");
pthread_cond_wait(&th_cnd, &th_mtx);
return 0;
}
void *threader(void *ptr) {
char *label = (char *) ptr;
int fd, i;
fd = open(label, O_APPEND | O_CREAT | O_WRONLY, 0666);
if (fd < 0) err(1, "open failed");
close(STDOUT_FILENO);
dup2(fd, STDOUT_FILENO);
for (i = 0; i < 5; i++) { puts(label); usleep(100000); }
pthread_cond_broadcast(&th_cnd);
return (void *) 0;
}
이 코드로 인해 발생할 수 있는 문제
% CFLAGS=-lpthread make thread-io-redirect
cc -lpthread thread-io-redirect.c -o thread-io-redirect
% rm thread.*
zsh: no matches found: thread.*
% ./thread-io-redirect
% stat -f '%N %z' thread.*
thread.x 0
thread.y 90
모든 출력이 입력됩니다 (또는 시스템 사용량에 따라 thread.y
전부 출력되거나 다른 극단적인 경우가 발생할 수 있음). thread.x
이는 표준 출력 리디렉션이 프로세스 전체에 적용된다는 것을 나타냅니다. (간단한 주장은 STDOUT_FILENO
프로세스당 하나의 설명자만 있고 유사한 함수는 puts(3)
해당 숫자 하나만 사용한다는 것입니다.)