시스템 프로그래밍 기본 개념
커널 공간과 사용자 공간
Linux 메모리 영역은 시스템 보안을 위해 두 영역으로 구분됩니다:
| 영역 | 권한 | 주요 기능 |
|---|---|---|
| 커널 공간 | 높은 권한(Ring 0) | 하드웨어 관리, 프로세스 스케줄링, 메모리 할당 |
| 사용자 공간 | 낮은 권한(Ring 3) | 응용 프로그램 실행, 직접 하드웨어 접근 불가 |
사용자 프로그램은 시스템 호출(System Call)을 통해 커널 서비스 요청
시스템 호출 유형
- 프로세스 제어: fork()(프로세스 생성), exec()(프로그램 실행), exit()(프로세스 종료)
- 파일 작업: open(), read(), write(), close()
- 프로세스 통신: pipe()(파이프), msgget()(메시지 큐)
- 메모리 관리: malloc()(사용자 영역), brk()(커널 힙 크기 조정)
핵심 기술 구현 예제
파일 입출력 작업
#include <fcntl.h>
#include <unistd.h>
#define BUFFER_CAPACITY 4096
int main(int args_count, char *args_arr[]) {
if (args_count != 3) {
write(STDOUT_FILENO, "사용법: 실행파일 원본 대상\n", 28);
return 1;
}
int origin_fd = open(args_arr[1], O_RDONLY);
if (origin_fd < 0) {
perror("원본 파일 오픈 실패");
return 1;
}
int target_fd = open(args_arr[2], O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (target_fd < 0) {
perror("대상 파일 오픈 실패");
close(origin_fd);
return 1;
}
char data_buffer[BUFFER_CAPACITY];
ssize_t bytes_read;
while ((bytes_read = read(origin_fd, data_buffer, BUFFER_CAPACITY)) > 0) {
if (write(target_fd, data_buffer, bytes_read) != bytes_read) {
perror("쓰기 오류");
break;
}
}
close(origin_fd);
close(target_fd);
if (bytes_read < 0) {
perror("읽기 오류");
return 1;
}
return 0;
}
프로세스 생성 제어
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t child_pid = fork();
if (child_pid < 0) {
perror("프로세스 생성 실패");
return 1;
}
if (child_pid == 0) {
printf("자식 프로세스 PID: %d, 부모 PID: %d\n", getpid(), getppid());
sleep(1);
printf("자식 종료\n");
} else {
printf("부모 프로세스 PID: %d, 자식 PID: %d\n", getpid(), child_pid);
wait(NULL);
printf("부모 종료\n");
}
return 0;
}
실습: 간단한 쉘 구현
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define INPUT_MAX 128
void get_user_input(char *user_cmd) {
printf("custom_shell> ");
fgets(user_cmd, INPUT_MAX, stdin);
user_cmd[strcspn(user_cmd, "\n")] = 0;
}
int main() {
char user_cmd[INPUT_MAX];
while (1) {
get_user_input(user_cmd);
if (strcmp(user_cmd, "exit") == 0) {
printf("쉘 종료\n");
break;
}
pid_t child_id = fork();
if (child_id < 0) {
perror("프로세스 생성 오류");
continue;
}
if (child_id == 0) {
execlp(user_cmd, user_cmd, NULL);
perror("명령 실행 실패");
exit(1);
} else {
wait(NULL);
}
}
return 0;
}
문제 해결 및 디버깅 기법
시스템 호출 오류 처리
모든 시스템 호출 실패 시 -1 반환, errno 변수 설정
- perror("오류 설명") 또는 strerror(errno)로 상세 오류 확인
- 예: open() 실패 시 "open: 해당 파일 없음" 출력
좀비 프로세스 방지
- wait() 또는 waitpid()로 자식 프로세스 종료 대기
- SIGCHLD 시그널 핸들러 등록하여 자동 정리
디버깅 도구
- gdb: C 프로그램 디버깅
gcc -g 소스파일.c -o 출력파일 gdb ./출력파일
- strace: 시스템 호출 추적
strace ./실행파일 매개변수