![프로그램이 종료된 후 exec가 출력을 인쇄하는 이유는 무엇입니까?](https://linux55.com/image/190225/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4%20%EC%A2%85%EB%A3%8C%EB%90%9C%20%ED%9B%84%20exec%EA%B0%80%20%EC%B6%9C%EB%A0%A5%EC%9D%84%20%EC%9D%B8%EC%87%84%ED%95%98%EB%8A%94%20%EC%9D%B4%EC%9C%A0%EB%8A%94%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F.png)
한 명령을 다른 명령으로 파이프하는 프로그램을 작성 중입니다. 입력은 명령줄에서 제공됩니다.
$ ./a.out ls '|' wc
c2 PID 6804
c1 PID 6803
PARENT PID 6802
$ 2 2 17
돌아온 후 프롬프트가 인쇄되는 이유는 무엇입니까? 이런 일이 발생하지 않도록 하는 방법이 있나요?
제가 작성한 코드는 다음과 같습니다.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
if(argc <= 1 )
{
printf("ERROR: No arguments passed\n");
printf("USAGE: ./pipe <command 1> | <command 2>\n");
return 1;
}
char * cmd1[50];
char * cmd2[50];
int cmd1_arg = 0;
int cmd2_arg = 0;
int pipe_num = 0;
for(int cla = 1; cla<argc; cla++)
{
if( !strcmp(argv[cla],"|") )
pipe_num++;
else if(pipe_num == 0)
cmd1[cmd1_arg++] = argv[cla];
else if(pipe_num == 1)
cmd2[cmd2_arg++] = argv[cla];
}
cmd1[cmd1_arg] = (char *)NULL;
cmd2[cmd2_arg] = (char *)NULL;
if(pipe_num != 1)
{
printf("ERROR: Insufficient arguments passed\n");
printf("USAGE: ./pipe <command 1> | <command 2>\n");
return 1;
}
int pipe_fd[2];
pipe(pipe_fd);
pid_t pid = fork();
if(pid == -1)
{
perror("FORK FAILED");
return 1;
}
if(pid != 0)
{
pid_t cmd_pid = fork();
if(cmd_pid == -1)
{
perror("FORK FAILED");
return 1;
}
if(cmd_pid != 0)
{
waitpid(pid,NULL,0);
waitpid(cmd_pid,NULL,WNOHANG);
printf("PARENT PID %d\n",getpid());
}
if(cmd_pid == 0)
{
printf("c2 PID %d\n",getpid());
close(pipe_fd[1]);
int stdin_fd = dup(0);
close(0);
dup(pipe_fd[0]);
if(execvp(cmd2[0],cmd2) == -1 ) perror("CMD2 FAIL");
close(0);
dup(stdin_fd);
}
}
if(pid == 0)
{
printf("c1 PID %d\n",getpid());
close(pipe_fd[0]);
int stdout_fd = dup(1);
close(1);
int test = dup(pipe_fd[1]);
if( execvp(cmd1[0],cmd1) == -1 ) perror("CMD1 FAIL");
close(1);
dup(stdout_fd);
}
return 0;
}
답변1
당신은:
waitpid(cmd_pid,NULL,WNOHANG);
이 옵션을 포함하면 프로세스가 아직 종료되지 않은 경우 프로세스가 종료될 때까지 기다리지 않도록 WNOHANG
지시하는 것입니다 .waitpid()
내 생각에는 그것을 포함하지 않으면 프로그램이 중단될 것이기 때문에 추가한 것 같습니다. 이는 원래 상위 프로세스에 여전히 파이프의 쓰기 끝을 가리키는 열린 파일 설명자가 있으므로 읽기 프로세스가 여전히 차단되어 해당 파이프에 대한 입력을 기다리고 있기 때문입니다.
파이프 파일 설명자를 닫고 WNOHANG
.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc <= 1) {
printf("ERROR: No arguments passed\n");
printf("USAGE: ./pipe <command 1> | <command 2>\n");
return 1;
}
char *cmd1[50];
char *cmd2[50];
int cmd1_arg = 0;
int cmd2_arg = 0;
int pipe_num = 0;
for (int cla = 1; cla < argc; cla++) {
if (!strcmp(argv[cla], "|")) {
pipe_num++;
} else if (pipe_num == 0) {
cmd1[cmd1_arg++] = argv[cla];
} else if (pipe_num == 1) {
cmd2[cmd2_arg++] = argv[cla];
}
}
cmd1[cmd1_arg] = NULL;
cmd2[cmd2_arg] = NULL;
if (pipe_num != 1) {
printf("ERROR: Insufficient arguments passed\n");
printf("USAGE: ./pipe <command 1> | <command 2>\n");
return 1;
}
int pipe_fd[2];
if (pipe(pipe_fd) < 0) {
perror("pipe");
return 1;
}
const pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
} else if (pid != 0) {
const pid_t cmd_pid = fork();
if (cmd_pid < 0) {
perror("fork");
return 1;
} else if (cmd_pid != 0) {
printf("PARENT PID %d\n", getpid());
close(pipe_fd[0]);
close(pipe_fd[1]);
if (waitpid(pid, NULL, 0) < 0) {
perror("waitpid");
}
if (waitpid(cmd_pid, NULL, 0) < 0) {
perror("waitpid");
}
} else {
printf("c2 PID %d\n", getpid());
if (dup2(pipe_fd[0], STDIN_FILENO) < 0) {
perror("dup2");
return 1;
}
close(pipe_fd[0]);
close(pipe_fd[1]);
if (execvp(cmd2[0], cmd2) < 0) {
perror("CMD2 FAIL");
return 1;
}
}
} else {
printf("c1 PID %d\n", getpid());
if (dup2(pipe_fd[1], STDOUT_FILENO) < 0) {
perror("dup2");
return 1;
}
close(pipe_fd[0]);
close(pipe_fd[1]);
if (execvp(cmd1[0], cmd1) < 0) {
perror("CMD1 FAIL");
return 1;
}
}
return 0;
}
이 프로그램을 실행하면 다음이 제공됩니다.
./a.out echo 1 2 3 '|' wc
PARENT PID 20412
c1 PID 20413
c2 PID 20414
1 3 6
$
답변2
원래 프로세스(하위 프로세스의 상위 프로세스)는 다음 명령을 실행합니다.
waitpid(pid,NULL,0);
waitpid(cmd_pid,NULL,WNOHANG);
printf("PARENT PID %d\n",getpid());
즉, 첫 번째 하위 프로세스가 종료될 때까지 기다리고 두 번째 하위 프로세스가 종료되는지 확인하기 위해 시스템 호출을 수행하지만 WNOHANG
실제로는 기다리지 않습니다. 그럼에도 불구하고 프로그램은 반환 값을 사용하지 않기 때문에 확인은 의미가 없습니다 waitpid()
. 그런 다음 상위 프로세스는 PID를 인쇄하고 계속해서 함수를 실행하고 다른 두 if
명령문을 전달하고 종료합니다 return 0
.
이 시점에서 두 번째 하위 프로세스가 아직 실행 중일 수도 있고 실행되지 않을 수도 있습니다. 명시적인 동기화가 없으면 상황이 발생하는 순서를 보장할 수 없습니다. 게다가 쉘은 프로그램에 의해 시작된 하위 프로세스에 대한 지식이 없으며 이를 기다리는 메커니즘도 없습니다. 기본 프로세스가 wait*()
이를 제공해야 합니다.
코드 작성을 계속하기 전에 복잡하게 중첩된 if 문을 제거하고 좀 더 명확한 구조에 투자하세요. 이러한 코드를 단순화하는 일반적인 방법은 하위 프로세스가 즉시 함수를 호출하도록 하거나 _exit()
함수가 반환된 직후에 함수를 호출하도록 하는 것입니다. 이제 exec*()
호출이 실패하면 자식도 종료하는 대신 동일한 코드를 계속 실행합니다.