리눅스: O_CLOEXEC 플래그

open() 함수의 플래그 중 O_CLOEXEC를 이해해봅니다.


리눅스의 open() 함수의 플래그에서 이해하기 힘들었던 O_CLOEXEC 플래그를 이해해 보겠습니다.

일단, 아래의 명령어를 통해 open() 함수의 Manual에서 O_CLOEXEC에 대한 설명을 읽어봅시다.

$ man 2 open

새로운 파일 디스크립터가 생성될 때, close-on-exec 플래그를 설정해주는 역할을 한다고 합니다. 그렇다면 close-on-exec 플래그가 무엇인지 알아야겠습니다.

기본적으로 리눅스 프로그램은, exec로 프로세스를 생성할 때 열어놓은 파일 디스크립터를 모두 물려줍니다. 하지만 close-on-exec 플래그가 설정되어 있으면, exec 계열 함수로 새로운 프로세스를 생성할 때 파일 디스크립터를 그 프로세스에게 물려주지 않습니다.

직접 확인해봅시다. 일단 간단한 확인을 위해 네임드 파이프 하나를 만들어줍시다.

mkfifo aaa

그리고 이 aaa라는 파이프를 열 프로그램을 2개 작성합니다. 하나는 aaaopen하는 과정에서 O_CLOEXEC를 세워 놓고, 하나는 세우지 않은 채로 작성합니다.

cloexec.c

#include <stdio.h>
#include <fcntl.h>

int main(void){
    open("aaa", O_RDWR | O_CLOEXEC);
    execl("YOUR_PATH/wr_to_pipe", "./wr_to_pipe", 0);
}
n_cloexec.c

#include <stdio.h>
#include <fcntl.h>

int main(void){
    open("aaa", O_RDWR);
    execl("YOUR_PATH/wr_to_pipe", "./wr_to_pipe", 0);
}

그리고, exec에 의해 실행되어 상속받은 파일 디스크립터에 write를 수행할 프로그램을 작성합니다.

main.c

#include <stdio.h>

int main(void){
    write(3, "TEST", 5);    // Write to File descriptor 3
    perror("write");
}

파일 디스크립터는 3번부터 열리기 때문에, 만약에 부모 프로세스로부터 파일 디스크립터를 상속받았다면 3번 디스크립터에 write를 수행할 수 있을 겁니다. 하지만 부모 프로세스가 3번 디스크립터에 파일을 open하는 과정에서 O_CLOEXEC 플래그를 올려 놓았다면, exec에 의해 실행되어도 3번 디스크립터를 물려받지 못해 write를 수행할 수 없을 겁니다. write가 성공적으로 수행되었는지 perror() 함수를 통해 확인해 봅시다.

세 개의 파일을 모두 컴파일한 후, cloexecn_cloexec를 실행합니다.

O_CLOEXEC를 올렸던 프로그램이 wr_to_pipe를 실행하면, 파일 디스크립터를 인식하지 못하고 write에 실패하는 것을 볼 수 있습니다.