Linux 시스템 프로그래밍 핵심 기술 및 사례

시스템 프로그래밍 기본 개념

커널 공간과 사용자 공간

Linux 메모리 영역은 시스템 보안을 위해 두 영역으로 구분됩니다:

영역권한주요 기능
커널 공간높은 권한(Ring 0)하드웨어 관리, 프로세스 스케줄링, 메모리 할당
사용자 공간낮은 권한(Ring 3)응용 프로그램 실행, 직접 하드웨어 접근 불가

사용자 프로그램은 시스템 호출(System Call)을 통해 커널 서비스 요청

시스템 호출 유형

  1. 프로세스 제어: fork()(프로세스 생성), exec()(프로그램 실행), exit()(프로세스 종료)
  2. 파일 작업: open(), read(), write(), close()
  3. 프로세스 통신: pipe()(파이프), msgget()(메시지 큐)
  4. 메모리 관리: 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 시그널 핸들러 등록하여 자동 정리

디버깅 도구

  1. gdb: C 프로그램 디버깅
    gcc -g 소스파일.c -o 출력파일
    gdb ./출력파일
  2. strace: 시스템 호출 추적
    strace ./실행파일 매개변수

태그: linux 시스템프로그래밍 시스템호출 파일입출력 프로세스

7월 22일 19:41에 게시됨