Linux

Linux) 시스템 콜(System Call): Process Management

하이오야이 2024. 4. 23. 19:11

시스템 콜

운영 체제 커널에 정의된 인터페이스로, 응용 프로그램에서 운영 체제의 기능을 시스템콜 핸들러의 도움을 받아 사용할 수 있게 해줍니다.

 시스템 콜의 종류

Process Management

fork()

새로운 프로세스를 생성하는 데 사용됩니다. 부모 프로세스에서 fork()를 호출하면, 커널은 현재 실행 중인 부모 프로세스의 정확한 사본을 만들어 새로운 자식 프로세스를 생성합니다. 

이때, 자식 프로세스는 부모 프로세스의 메모리, 파일 디스크립터, 환경 변수 등을 상속받습니다.

#include <unistd.h>
#include <stdio.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        // 자식 프로세스 코드
        printf("자식 프로세스\n");
    } else if (pid > 0) {
        // 부모 프로세스 코드
        printf("부모 프로세스\n");
    } else {
        // fork() 호출에 실패한 경우
        perror("fork failed");
        return 1;
    }
    return 0;
}

waitpid()

부모 프로세스가 자식 프로세스의 종료를 기다리는 데 사용됩니다. 부모 프로세스는 waitpid() 함수를 호출하여 특정 자식 프로세스의 종료를 기다리거나, 모든 자식 프로세스의 종료를 기다릴 수 있습니다.

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t pid;
    int status;

    // 자식 프로세스 생성
    pid = fork();

    if (pid < 0) {
        // fork 실패
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 자식 프로세스
        printf("자식 프로세스가 시작됨\n");
        sleep(2); // 예시를 위해 2초간 대기
        printf("자식 프로세스가 종료됨\n");
        return 42; // 종료 코드 42 반환
    } else {
        // 부모 프로세스
        printf("부모 프로세스가 시작됨\n");

        // 자식 프로세스의 종료를 대기
        waitpid(pid, &status, 0);

        if (WIFEXITED(status)) {
            printf("자식 프로세스가 정상적으로 종료됨, 종료 코드: %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("자식 프로세스가 시그널에 의해 종료됨, 시그널 번호: %d\n", WTERMSIG(status));
        }

        printf("부모 프로세스가 종료됨\n");
    }

    return 0;
}

 

execve()

새로운 프로세스 이미지를 현재 실행 중인 프로세스에 로드하여 그 프로세스 이미지의 실행하는 시스템 콜입니다.

#include<stdio.h>

int main(){
	printf("hello world\n");
    return 0;
}

위에 코드를 컴파일하여 printHello 이름을 가진 프로세스 이미지를 생성해줍니다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    // 새로운 프로세스 이미지의 경로
    char *path = "/home/qwer1234/printHello";
    // 새로운 프로세스 이미지의 인수들
    char *args[] = { "printHello", NULL };
    // 환경 변수
    char *envp[] = { NULL };

    // 새로운 프로세스 이미지를 실행
    // 첫 번째 인자: 프로세스 이미지의 경로
    // 두 번째 인자: 프로세스 이미지의 인수 배열
    // 세 번째 인자: 환경 변수 배열
    execve(path, args, envp);

    // 만약 execve 함수가 정상적으로 실행되지 않으면 아래 코드가 실행될 것임
    perror("execve"); // 에러 메시지 출력
    return EXIT_FAILURE; // 실패 반환
}

exit()

프로세스가 종료되는 시점에서 호출되는 함수

프로세스의 자원을 동시에 해제 시킵니다.

 

정상적으로 프로세스가 종료 되었다면, 0을 인자로 전달합니다.

그렇지 않다면 0이 아닌 다른 값(종료 코드)을 인자로 전달합니다.

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("프로그램이 시작되었습니다.\n");

    // 예상치 못한 오류 발생 시 비정상 종료
    int *ptr = NULL;
    if (ptr == NULL) {
        fprintf(stderr, "오류: 포인터가 NULL입니다.\n");
        exit(1); // 비정상 종료, 종료 코드 1 반환
    }

    // 프로그램이 여기까지 도달하지 못함
    printf("프로그램이 종료되었습니다.\n");

    return 0;
}

kill()

프로세스에 시그널을 보내는 데 사용됩니다.

다른 프로세스에게 특정 동작을 유도하거나 프로세스를 종료하는 데 사용됩니다.

#include <signal.h>

int kill(pid_t pid, int sig);

kill() 함수의 프로토타입니다.

pid_t 파라미터에 프로세스 아이디를 전달하고, sig 파라미터에 시그널의 종류를 전달합니다.

시그널의 종료로는 SIGKILL, SIGTERM, SIGINT등이 있습니다.

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

int main() {
    pid_t pid;

    // 자식 프로세스 생성
    pid = fork();

    if (pid < 0) {
        // fork 실패
        perror("fork failed");
        return EXIT_FAILURE;
    } else if (pid == 0) {
        // 자식 프로세스
        printf("자식 프로세스가 시작됨\n");

        // 자식 프로세스를 다른 프로그램으로 대체
        char *args[] = {"ls", "-l", NULL};
        execvp(args[0], args);		
        
        // execvp 함수가 성공적으로 실행되면 이후 코드는 실행되지 않음
        perror("execvp failed");
        
        printf("자식 프로세스를 종료시킵니다.\n");
        
        return EXIT_FAILURE;
    } else {
        // 부모 프로세스
        printf("부모 프로세스가 시작됨\n");
		// 자식 프로세스를 종료
		kill(pid, SIGKILL);
        
        // 자식 프로세스의 종료를 대기
        waitpid(pid, NULL, 0);
        
        printf("부모 프로세스가 종료됨\n");
    }

    return 0;
}