파일 조작에 대한 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.
https://reakwon.tistory.com/233
fcntl
파일을 열었는데 그 속성을 바꾸려면 어떻게 할까요? fcntl함수는 이미 열린 파일의 속성들을 변경할 수 있습니다.
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
fd : 파일의 속성을 조회하거나 변경할 파일 디스크립터입니다.
cmd : 커맨드입니다. cmd 종류는 아래와 같습니다.
cmd | 설명 |
F_GETFL | fd에 대한 파일 상태 속성들을 반환값으로 돌려줍니다. O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH는 개별적으로 판정할 수 없고 ACCMODE라는 마스크를 이용하여 알아낼 수 있습니다. 이는 아래의 예제를 통해서 알아보도록 하지요. |
F_SETFL | 파일 상태 속성들을 세번째 인수로 받아 설정합니다. O_APPEND, O_NONBLOCK, O_SYNC, O_DSYNC, O_RSYNC, O_FSYNC, O_ASYNC 속성만 변경할 수 있습니다. 읽기, 쓰기 관련 플래그(O_WRONLY, O_RDONLY, O_RDWR)은 조회할 수는 있지만 변경할 수 없음을 유의하세요. |
F_GETOWN | 현재 SIGIO, SIGURG 신호를 받도록 설정된 프로세스 ID 혹은 프로세스 그룹 ID를 돌려줍니다. 비동기 입출력과 관련이 있습니다. |
F_SETOWN | SIGIO와 SIGURG신호를 받도록 시정된 프로세스 ID나 프로세스 그룹 ID를 설정합니다. |
F_DUPFD | 파일 서술자 fd를 복제합니다. 새 파일 서술자를 함수의 반환값으로 돌려줍니다. 이때 반환된 새 서술자는 FD_CLOEXEC속성이 해제된 상태입니다. 그렇기 때문에 exec류의 함수로 다른 명령을 실행했을때 열린 상태로 남아있습니다. |
F_DUPFD_CLOEXEC | 파일 서술자를 복제하고 새 파일 서술자에 관련된 FD_CLOEXEC를 설정합니다. 역시 새 파일 서술자가 반환됩니다. |
F_GETFD | fd에 대한 파일 서술자 플래그들을 반환합니다. 정의되어 있는 파일 서술자 flag는 FD_CLOEXEC입니다. |
F_SETFD | 서술자 플래그들을 설정합니다. 셋째 인자에 그 플래그를 전달합니다. |
위 함수는 cmd에 따라 반환값이 다르고 실패시 -1을 반환하게 됩니다.
속성 조회, 변경 예제
아래는 파일의 속성을 조회해보고 O_APPEND 속성을 추가하여 다시 조회하는 예제입니다.
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
void printAccessMode(int flags){
switch(flags & O_ACCMODE){
case O_RDONLY:
printf("read only\n");
break;
case O_WRONLY:
printf("write only\n");
break;
case O_RDWR:
printf("read write\n");
break;
}
printf("append %s \n", (flags & O_APPEND) ? "O":"X");
printf("nonblocking %s \n", (flags & O_NONBLOCK) ? "O":"X");
printf("\n");
}
int main(){
int fd = open("test.txt", O_RDONLY|O_CREAT, 0666);
int flags = fcntl(fd, F_GETFL,0);
printAccessMode(flags);
flags |= O_APPEND;
fcntl(fd, F_SETFL, flags);
flags = fcntl(fd, F_GETFL, 0);
printAccessMode(flags);
}
주의해야할 점은 파일 서술자 플래그, 파일 상태 플래그를 수정할때는 반드시 먼저 플래그를 얻어오고(GET) 얻어온 값을 통해 변경(SET)해야합니다. 그렇지 않고 바로 SET하면 이전에 설정했던 값은 지워질 수 있습니다.
결과
# ./a.out
read only
append X
nonblocking X
read only
append O
nonblocking X
Non-blocking 예제
표준 입력(fd 0)으로 입력을 받는데, 이것을 non-blocking으로 하고 싶을때 fcntl을 통해서 구현할 수 있습니다. 바로 O_NONBLOCK을 통해서 실현할 수 있죠.
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
int main(){
char buf[128] = {0,};
int fd = 0;
int flags = fcntl(fd, F_GETFL,0);
flags |= O_NONBLOCK;
//fcntl(fd, F_SETFL, flags);
read(fd, buf, sizeof(buf));
printf("buf : %s \n", buf);
}
위의 코드를 그대로 실행하면 우리가 알고 있듯, 입력을 받을때까지 프로그램은 대기하게 됩니다. 사용자가 입력을 하게 되면 그때 입력 내용을 출력하고 프로그램이 끝이 납니다.
# ./a.out
Hello!!
buf : Hello!!
이제 주석을 해제하면 사용자 입력을 기다리지 않고 바로 버퍼를 출력하고 끝납니다.
# ./a.out
buf :
이것을 조금만 응용하면 사용자가 입력할 시간을 주고 제한 시간내에 입력이 없으면 그에 따른 로직을 도는 프로그램을 짤 수도 있을 겁니다.
CLOEXEC 예제
어떤 파일을 열어서 fd를 얻었고 exec류의 함수로 다른 프로그램을 실행하려고 합니다. 이때 다른 프로그램은 이미 열려진 fd를 사용하지 않음에도 fd를 그대로 가져가게 되죠. 다음의 코드가 그런 현상을 보여줍니다.
파일을 오픈한 후 execl함수로 loop이라는 프로그램을 실행합니다.
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
int fd = open("test.txt",O_RDONLY|O_CREAT,0666);
int flags = fcntl(fd, F_GETFD, 0);
flags |= FD_CLOEXEC;
//fcntl(fd, F_SETFD, flags);
execl("/root/examples/file_test/loop","./loop",0);
}
loop프로그램은 다음의 코드로 만들어졌습니다. pid출력하고 무한루프를 돕니다. 별거없죠?
#include <stdio.h>
#include <unistd.h>
int main(){
printf("loop start pid:%d\n",getpid());
while(1);
}
두개를 컴파일하고 무한 루프 프로그램말고 execl을 호출하는 프로그램을 실행해보도록 하지요.
# ./a.out &
[2] 6808
# loop start pid:6808
이제 해당 pid의 fd를 보시기 바랍니다. 그러면 loop 프로그램에서 열린 fd를 볼 수 있는데, 맨 아래의 test.txt도 열려있음을 확인할 수 있습니다.
# ls -l /proc/6808/fd
합계 0
lrwx------. 1 root root 64 6월 27 18:54 0 -> /dev/pts/1
lrwx------. 1 root root 64 6월 27 18:54 1 -> /dev/pts/1
lrwx------. 1 root root 64 6월 27 18:54 2 -> /dev/pts/1
lr-x------. 1 root root 64 6월 27 18:54 3 -> /root/examples/file_test/test.txt
이 fd를 exec시에 닫고 싶다면 위의 주석을 해제하고 다시 실행해보세요.
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
int fd = open("test.txt",O_RDONLY|O_CREAT,0666);
int flags = fcntl(fd, F_GETFD, 0);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
execl("/root/examples/file_test/loop","./loop",0);
}
그러면 아래와 같이 test.txt가 loop 프로세스에서 닫혀져있음을 알 수 있습니다.
# ./a.out &
[3] 6893
# loop start pid:6893
#
# ls -l /proc/6893/fd
합계 0
lrwx------. 1 root root 64 6월 27 18:57 0 -> /dev/pts/1
lrwx------. 1 root root 64 6월 27 18:57 1 -> /dev/pts/1
lrwx------. 1 root root 64 6월 27 18:57 2 -> /dev/pts/1
지금까지 fcntl 설명과 fcntl을 이용해서 파일의 속성을 바꾸는 예제를 보았습니다.
'컴퓨터 > 운영체제(주로 리눅스)' 카테고리의 다른 글
[리눅스] 다중입출력 - select개념과 설명과 예제 (1) | 2020.11.26 |
---|---|
[리눅스] 재지정, 리다이렉션(redirection: >, <)과 파이프(|) 개념과 쉬운 설명 (3) | 2020.07.09 |
[리눅스] 디스크 관련 명령어 df, du 명령어 사용법 (0) | 2020.06.28 |
[리눅스] getopt 실행 명령 인자(명령어 옵션) 파싱하기 (0) | 2020.06.25 |
[리눅스] 프로세스 상태 얻어오기 (wait, waitpid 함수 ) (2) | 2020.05.24 |