Shell: 두 판 사이의 차이
youngwiki
편집 요약 없음 |
|||
| (같은 사용자의 중간 판 2개는 보이지 않습니다) | |||
| 1번째 줄: | 1번째 줄: | ||
상위 문서: [[ | 상위 문서: [[Exceptional Control Flow]] | ||
==개요== | ==개요== | ||
| 6번째 줄: | 6번째 줄: | ||
* csh/tcsh : BSD Unix C 쉘 | * csh/tcsh : BSD Unix C 쉘 | ||
* bash : Bourne-Again Shell (리눅스 기본 쉘) | * bash : Bourne-Again Shell (리눅스 기본 쉘) | ||
Shell은 | Shell은 이용자의 명령어를 입력받고, 이를 실행시키는 식으로 작동한다. | ||
==예시== | ==예시== | ||
| 40번째 줄: | 40번째 줄: | ||
/* argv가 내장된 명령어인지 확인하고 내장된 명령어라면 내부에서 실행한다. */ | /* argv가 내장된 명령어인지 확인하고 내장된 명령어라면 내부에서 실행한다. */ | ||
if (!builtin_command(argv)) { /* 내장된 명령어가 아닌 경우 */ | if (!builtin_command(argv)) { /* 내장된 명령어가 아닌 경우, 내장된 명령어면 builtin_command() 함수가 실행한다. */ | ||
if ((pid = fork()) == 0) { /* 자식 프로세스를 생성한다. */ | if ((pid = fork()) == 0) { /* 자식 프로세스를 생성한다. */ | ||
if (execve(argv[0], argv, environ) < 0) { /* 자식 프로세스 내에서 실제 명령어를 실행시킨다. */ | if (execve(argv[0], argv, environ) < 0) { /* 자식 프로세스 내에서 실제 명령어를 실행시킨다. */ | ||
2025년 4월 10일 (목) 16:42 기준 최신판
상위 문서: Exceptional Control Flow
개요
Shell이란 사용자 명령을 실행하는 애플리케이션 프로그램이다. 다음은 shell의 대표적인 예시이다.
- sh : 오리지널 Unix 쉘 (Stephen Bourne, AT&T Bell Labs, 1977)
- csh/tcsh : BSD Unix C 쉘
- bash : Bourne-Again Shell (리눅스 기본 쉘)
Shell은 이용자의 명령어를 입력받고, 이를 실행시키는 식으로 작동한다.
예시
int main() {
char cmdline[MAXLINE]; /* 명령어를 입력받을 버퍼이다. */
while (1) {
/* read */
printf("> ");
Fgets(cmdline, MAXLINE, stdin); /* 명령어를 입력받는다. */
if (feof(stdin))
exit(0);
/* evaluate */
eval(cmdline); /* 명령어를 실행시킨다 */
}
}
void eval(char *cmdline) { /* cmdline은 사용자가 입력한 명령어 문자열이다. */
char *argv[MAXARGS]; /* cmdline을 공백을 기준으로 쪼개어 저장하는 문자열 배열이다. */
char buf[MAXLINE]; /* cmdline을 그대로 저장하는 버퍼 */
int bg; /* 실행할 명령이 백그라운드 실행인지 여부를 나타내는 플래그이다. */
pid_t pid; /* 새로 생성한 자식 프로세스의 PID를 저장 */
strcpy(buf, cmdline); /* cmdline을 buf에 복사한다. */
/* cmdline을 공백단위로 분리하여 이를 argv에 저장한다.
명령어 뒤에 &가 붙어있다면 백그라운드 실행이라는 뜻이므로 bg의 플래그를 1로 설정한다. */
bg = parseline(buf, argv);
if (argv[0] == NULL) /* 엔터만 입력된 경우이면 아무런 작업도 하지 않는다. */
return; /* Ignore empty lines */
/* argv가 내장된 명령어인지 확인하고 내장된 명령어라면 내부에서 실행한다. */
if (!builtin_command(argv)) { /* 내장된 명령어가 아닌 경우, 내장된 명령어면 builtin_command() 함수가 실행한다. */
if ((pid = fork()) == 0) { /* 자식 프로세스를 생성한다. */
if (execve(argv[0], argv, environ) < 0) { /* 자식 프로세스 내에서 실제 명령어를 실행시킨다. */
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg) { /* foreground 실행 */
int status;
if (waitpid(pid, &status, 0) < 0) /* foreground 작업인 경우 부모 프로세스는 해당 작업의 종료를 기다린다. */
unix_error("waitfg: waitpid error");
} else { /* background 실행 */
printf("%d %s\n", pid, cmdline); /* waitpid()를 호출하지 않고 PID만 출력 후 즉시 다음 명령을 받을 준비를 한다.*/
}
}
return;
}
해당 shell 프로그램은 foreground job에 대하여 종료를 기다리고 종료되는 즉시 이를 reap한다. 하지만 background job에 대해서는 다음과 같은 문제가 발생한다.
- 자식 프로세스가 종료될 경우 zombie 상태가 된다.
- shell이 종료되지 않을 경우 이를 reap하지 않고, 이로 인해 memory leak이 발생한다.
이를 해결할 수 있는 방법이 바로 Exceptional Control Flow이다. 커널은 background 프로세스가 종료될 때 signal을 보내서 이를 shell에 알릴 수 있다. Unix에서는 이를 이용하여 zombie들을 적절히 reap할 수 있다.