https://github.com/sumin-world/suminworld-system-lab/tree/main/shell
suminworld-system-lab/shell at main · sumin-world/suminworld-system-lab
System programming & networking lab (C, Linux, OSTEP practice) - sumin-world/suminworld-system-lab
github.com
전체 코드는 위에서 확인 가능합니다!
Foreground / Background 프로세스와 시그널 처리
Foreground (전경)
- 현재 사용자와 상호작용하고 있는 프로그램
- 키보드 입력을 받는 프로그램
- 터미널에서 "지금 실행 중"인 상태
Background (배경)
- 뒤에서 조용히 실행되는 프로그램
- 키보드 입력을 받지 않음
- & 기호로 실행하거나, Ctrl+Z로 보낸 프로그램들
실행 예시
myshell$ ls # ls가 foreground에서 실행
myshell$ sleep 100 & # sleep이 background에서 실행 (&때문에)
myshell$ vim file.txt # vim이 foreground에서 실행
# Ctrl+Z 누르면
[1]+ Stopped vim file.txt
myshell$ # vim이 background로 이동, 쉘이 다시 foreground
프로세스 상태 전환 (Job Control)
┌──────────────┐
│ Foreground │
│ (쉘이 기다림)│
└──────┬───────┘
│
Ctrl+Z │ 종료/완료
(SIGTSTP 신호) │
▼
┌──────────────┐
│ Stopped │
│ (일시정지) │
└──────┬───────┘
fg │ │ bg
(다시 전면) │ (백그라운드 실행)
▼
┌──────────────┐
│ Background │
│ (쉘은 자유) │
└──────────────┘
상태별 설명
- Foreground: 프로그램이 화면(터미널)을 점유하고, 쉘은 입력을 기다리지 않음
- Stopped: Ctrl+Z로 멈춘 상태 (쉘 프롬프트 돌아옴, 프로그램은 메모리 안에 그대로 있음)
- Background: & 또는 bg로 실행됨. 프로그램은 뒤에서 계속 돌아가고, 쉘은 입력을 즉시 받을 수 있음
fg_pgid 변수의 역할
fg_pgid는 foreground process group ID의 줄임말로, 지금 터미널을 점유하고 있는 프로세스 그룹의 ID를 저장합니다.
상태 해석
- fg_pgid = 0: foreground에 아무 프로그램도 없음 (쉘만 터미널 사용)
- fg_pgid > 0: 어떤 프로그램이 foreground에서 실행 중
왜 "그룹 ID"일까?
단순히 프로세스 하나만 실행되는 게 아니라, vim | grep abc | less 같은 파이프라인은 여러 프로세스로 이루어집니다. 이들을 묶어서 하나의 프로세스 그룹으로 관리해야 하기 때문입니다.
시그널 처리 설정 코드 분석
기본 구조
struct sigaction sa_int = {0}, sa_tstp = {0}, sa_chld = {0};
이 코드는 각각 다른 시그널을 처리할 "규칙"을 담는 구조체를 만든 것입니다:
- sa_int → SIGINT (Ctrl+C) 처리용
- sa_tstp → SIGTSTP (Ctrl+Z) 처리용
- sa_chld → SIGCHLD (자식 프로세스 종료 알림) 처리용
시그널 핸들러 등록
sa_int.sa_handler = sigint_handler;
sigemptyset(&sa_int.sa_mask);
sa_int.sa_flags = SA_RESTART;
sigaction(SIGINT, &sa_int, NULL);
각 줄의 의미:
- sa_int.sa_handler = sigint_handler;
- "만약 SIGINT가 오면 → sigint_handler()라는 함수를 실행해라"
- sigemptyset(&sa_int.sa_mask);
- 시그널을 처리하는 동안 추가로 막을 시그널이 없음을 지정
- sa_int.sa_flags = SA_RESTART;
- 시그널 처리 중에 read(), write() 같은 시스템 콜이 중단되지 않고 자동으로 재시도되게 함
- sigaction(SIGINT, &sa_int, NULL);
- "앞으로 SIGINT(=Ctrl+C) 들어오면 sa_int 규칙대로 처리해라" 등록
struct sigaction 구조체 이해
구조체 정의 (리눅스 glibc 기준)
struct sigaction {
void (*sa_handler)(int); // 시그널 오면 실행할 함수
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask; // 처리 중 block할 시그널 집합
int sa_flags; // 옵션 플래그
void (*sa_restorer)(void); // (거의 안 씀, 옛날용)
};
핵심: sa_handler
- void (*sa_handler)(int); → 함수 포인터
- 의미: "인자로 int 하나를 받는, 반환형이 void인 함수의 주소"
- 이 필드에 시그널 처리 함수를 등록
구조체 사용 예시
// 시그널 핸들러 함수 정의
void sigint_handler(int sig) {
printf("Ctrl+C 눌렀네! (시그널 번호=%d)\n", sig);
}
// 구조체에 핸들러 등록
struct sigaction sa_int = {0};
sa_int.sa_handler = sigint_handler;
주요 시그널 비교
시그널 단축키 기본 동작 결과
| SIGINT | Ctrl + C | 프로세스 종료 | 프로그램 죽음 |
| SIGTSTP | Ctrl + Z | 프로세스 일시정지 | 프로그램 멈춤, 다시 실행 가능 |
핵심 차이점
- Ctrl+C (SIGINT) = 완전 중단 (죽임)
- Ctrl+Z (SIGTSTP) = 일시정지 (살려둠)
SIGINT ≠ 터미널 종료
SIGINT는 터미널 자체를 종료하는 게 아니라, 현재 foreground에 있는 프로세스 그룹에 시그널을 보내는 것입니다.
myshell$ cat hello # (지금 cat은 입력 대기중)
^C # Ctrl+C 누름
myshell$ # cat은 종료되었고, 쉘은 계속 살아있음
마무리
이 시그널 처리 메커니즘을 통해 쉘은:
- 사용자의 키보드 입력(Ctrl+C, Ctrl+Z)을 감지
- 적절한 프로세스 그룹에 시그널 전달
- Foreground/Background 프로세스 상태 관리
- Job control 기능 제공
-- 2025.09.12.(금) 05:02
init_shell() 함수 한번 더 정리하기
print_prompt() 함수로 넘어가기
'system' 카테고리의 다른 글
| 🛡️SGI 서울보증 랜섬웨어 사건 정리 & 리눅스 백신 실습 (1) | 2025.09.16 |
|---|---|
| [Linux Signal] - 쉘, 프로세스 제어, 시그널 핸들링 (0) | 2025.09.14 |
| [시스템 프로그래밍] myshell.c 코드 분석 - 1 (1) | 2025.09.11 |
| [시스템 프로그래밍] Tiny Shell 프로젝트: 잡 컨트롤, 시그널, 레이스 컨디션 다루기 (1) | 2025.09.09 |
| [시스템 프로그래밍] execve() 시스템 콜 C vs 어셈블리 비교 분석 (0) | 2025.09.08 |