3장 파일 다루기 한빛미디어(주)
서론 시스템 호출 (system call) 파일을 다루기 위한 시스템 호출/표준 라이브러리 함수 함수 의미 open 사용자가 작성한 프로그램이 커널 수준의 서비스를 요청한다. 파일을 다루기 위한 시스템 호출/표준 라이브러리 함수 함수 의미 open 이미 존재하는 파일을 읽기 또는 쓰기용으로 열거나, 새로운 파일을 생성하여 연다. creat 새로운 파일을 생성하여 연다. close open 또는 creat로 열려진 파일을 닫는다. read 열려진 파일로부터 데이터를 읽어 들인다. write 열려진 파일에 데이터를 쓴다. lseek 파일 안에서 읽기/쓰기 포인터를 지정한 바이트 위치로 이동한다. unlink/remove 파일을 삭제한다.
서론 유닉스에서 파일이란 파일 입출력 순서 파일기술자(file descriptor) 단순히 연속적인 바이트(또는 문자)들로 취급 파일 입출력 순서 (open / creat) → (read / write / lseek) → close 파일기술자(file descriptor) file descriptor: 오픈한 각 파일에 붙여진 번호 file descriptor를 이용해 입출력할 파일을 지정한다. 아래 file descriptor는 미리 정해져 있음 0 : 표준입력 (standard input) 1 : 표준출력 (standard output) 2 : 표준에러 (standard error) System Programming
OPEN 03 기존의 파일을 오픈하거나 새로운 파일을 생성한 후에 오픈한다. 새로운 파일을 생성할 때는 open이나 creat를 사용한다. #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags, [mode_t mode]); pathname 오픈할 파일의 경로 이름을 가지고 있는 문자열의 포인터이다. flags 파일의 오픈 방식을 지정한다. mode 대부분의 경우 생략할 수 있는 값으로 새롭게 생성하는 파일의 초기 접근 권한을 지정한다. 반환값 정상적으로 파일을 오픈하게 되면 파일기술자를 반환하게 된다. 파일 오픈이 실패할 경우 -1을 반환한다.
OPEN 03 pathname /* 절대 경로로 지정 */ 오픈하려는 파일의 경로 이름 상대 경로, 절대 경로 문자열 상수, 문자열형의 포인터 /* 절대 경로로 지정 */ filedes = open("/home/lsp/data.txt", O_RDONLY); /* 상대 경로로 지정 */ filedes = open("data.txt", O_RDONLY); … /* 문자열을 담고 있는 배열 */ char pathname[] = "data.txt"; /* 배열의 포인터로 경로를 지정 */ filedes = open(pathname, O_RDONLY);
OPEN 03 flags mode O_RDONLY 읽기만 가능한 상태로 접근한다. O_WRONLY 파일을 오픈하고 난 후의 접근 방식 읽기 전용, 쓰기 전용, 읽기 쓰기용 O_RDONLY 읽기만 가능한 상태로 접근한다. O_WRONLY 쓰기만 가능한 상태로 접근한다. O_RDWR 읽기, 쓰기 모두 가능한 상태로 접근한다. mode 파일의 접근 권한을 의미한다. 이미 존재하는 파일을 오픈하여 사용할 경우 생략한다. 새로운 파일을 생성할 때 적용한다.
OPEN 03 반환값 호출이 성공하면 음이 아닌 정수형의 값을 반환한다. 파일기술자 (file descriptor) 프로그램 내에서 파일 기술자는 중복되지 않는다. 0, 1, 2는 반환값으로 나타나지 않는다. 0, 1, 2는 각각 표준 입력, 표준 출력, 표준 에러로 사용된다. 반환값이 -1이면 파일 오픈에 실패했다. 예를 들어, 지정한 파일이 존재하지 않거나 접근 권한이 없는 경우
OPEN 03 open 호출 실패에 대한 대비 시스템 호출이 실패했을 경우를 고려한 프로그램 코드 … 호출 실패시 적절한 대응이 있어야 한다. 시스템 호출이 실패했을 경우를 고려한 프로그램 코드 … filedes = open("data.txt", O_RDONLY); /* filedes가 -1이면 open 호출이 실패했다. */ if(filedes == -1) { printf("file open error!\n"); exit(1); }
close 04 오픈 상태의 파일을 닫는다 #include <unistd.h> int close(int filedes); filedes 이전에 open이나 creat에 의해 오픈된 파일의 파일기술자이다. 반환값 작업이 성공할 경우 0이 반환되며, 실패할 경우 -1이 반환된다. open이나 creat에 의해 오픈된 파일을 닫는다. 할당 받은 파일 기술자를 반환한다. 오픈된 파일은 사용이 끝나면 반드시 닫아주어야 한다. 하나의 프로세스가 동시에 오픈할 수 있는 파일의 수는 제한되어 있음 시스템 차원에서 동시에 오픈할 수 있는 파일의 수는 제한되어 있음
close 04 /* 파일을 읽기 전용 상태로 오픈한다. */ filedes = open("data.txt", O_RDONLY); … /* data.txt를 사용하는 코드가 위치 */ close(filedes); 오픈된 파일은 사용이 끝난 후 반드시 닫아준다. 오픈된 파일을 닫지 않고 프로그램이 종료한 경우 프로그램이 종료할 때 오픈된 파일은 커널에 의해 자동으로 닫힌다. 이런 사실을 알고 있더라도 사용된 파일은 마지막에 닫아주는 것이 좋다.
open으로 새로운 파일 생성하기 05 O_CREAT 플래그 open으로 새로운 파일을 생성할 때 사용한다. filedes = open(“/tmp/tmpfile.txt”, O_RDWR | O_CREAT, 0644); O_RDWR 새롭게 생성된 파일은 읽기와 쓰기가 가능하다. O_CREAT /tmp/tmpfile.txt가 존재하지 않을 경우 새롭게 생성한다. 새로운 파일을 생성할 때 반드시 사용해야 한다. 0644 새롭게 생성된 파일의 접근 권한은 0644이다. rw-r--r--
ex03-01.c 05 #include <fcntl.h> #include <stdlib.h> #include <stdio.h> int main(void) { int filedes; char pathname[] = "temp.txt"; filedes = open(pathname, O_RDWR|O_CREAT, 0644); if (filedes == -1) printf("file open error!\n"); exit(1); } close(filedes); $ ls -l temp.txt ls: temp.txt: No such file or directory $ ./a.out -rw-r--r-- 1 schan schan 0 Aug 15 08:32 temp.txt
open으로 새로운 파일 생성하기 05 O_EXCL 플래그 이미 존재하는 파일을 O_CREAT 플래그를 사용하여 오픈할 때O_EXCL 플래그가 파일의 오픈을 막는다. (즉 오픈이 실패한다.) 이런 경우 사용한다.. 이미 존재하는 파일을 O_CREAT 플래그를 사용하여 오픈하면 기존의 내용을 수정하는 실수를 범할 수 있다. O_EXCL 플래그를 사용하여 오픈 자체를 실패하게 한다. filedes = open(pathname, O_CREAT | O_RDWR | O_EXCL, 0644); pathname에 해당하는 파일이 존재하지 않으면 새롭게 생성한 후에 읽기와 쓰기가 가능한 상태로 만들고 접근 권한은 0644이다. 이미 존재하면 open이 실패한다.
open으로 새로운 파일 생성하기 05 open에서 자주 사용하는 플래그 파일 오픈에 사용되는 플래그 O_RDONLY 파일을 읽기 전용으로 오픈한다. 읽기 이외의 다른 작업을 수행할 수 없다. O_WRONLY 파일을 쓰기 전용으로 오픈한다. 쓰기 이외의 다른 작업을 수행할 수 없다. O_RDWR 파일을 읽기와 쓰기가 동시에 가능한 상태로 오픈한다. O_CREAT 지정한 경로의 파일이 존재하지 않으면 새롭게 생성한 후 오픈한다. 지정한 경로의 파일이 존재하면 지정한 상태로 오픈한다. O_EXCL 지정한 경로의 파일이 존재하지 않으면 새롭게 생성하나, 지정한 경로의 파일이 존재하면 open 호출을 실패한다. (※O_CREAT 플래그와 함께 사용해야 한다.) O_APPEND 파일을 오픈한 직후에 읽기/쓰기 포인터의 위치를 파일 내용의 마지막 바로 뒤로 이동한다. O_TRUNC 파일을 오픈한 직후에 읽기/쓰기 포인터의 위치를 파일 내용의 첫 부분으로 이동한다.
open으로 새로운 파일 생성하기 05 filedes = open(filename, O_RDWR); filedes = open(filename, O_RDONLY | O_CREAT); -filename으로 지정한 파일이 존재할 경우 읽기 전용으로 오픈 -만약 파일이 존재하지 않으면 새롭게 생성한 후 읽기 전용으로 오픈 (파일의 권한을 생략했기 때문에 기본 값으로 초기권한이 설정) filedes = open(filename, O_WRONLY | O_CREAT, 0644); -filename으로 지정한 파일이 존재할 경우 쓰기 전용으로 오픈 -만약 파일이 존재하지 않으면 새롭게 생성한 후 쓰기 전용으로 오픈 -초기 권한이 0644로 설정 filedes = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644); -filename으로 지정한 파일이 존재하면 open의 수행이 실패 -만약에 파일이 존재하지 않으면 초기권한 0644로 새롭게 파일을 생성 (쓰기 전용으로 오픈)
creat 05 새로운 파일을 생성한다 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int creat(const char *pathname, mode_t mode); pathname 오픈할 파일의 경로 이름을 가지고 있는 문자열의 포인터이다. mode 새롭게 생성하는 파일의 초기 접근 권한을 지정한다. creat는 존재하지 않는 파일을 새롭게 생성하는 것이 사용 목적이기 때문에 생략할 수 없다. 반환값 정상적으로 파일을 오픈하게 되면 파일 기술자를 반환하게 된다. 파일 오픈이 실패할 경우 -1을 반환한다. creat는 open을 다음의 플래그와 함께 사용하는 것과 같다. O_WRONLY, O_CREAT, O_TRUNC
creat 05 이미 존재하는 파일을 지정하여 creat를 사용하는 경우 해당 파일을 오픈함과 동시에 파일이 가지고 있는 데이터를 모두 삭제한다 filedes = creat(pathname, 0644); … filedes = open(pathmame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
read, write 06 read는 파일기술자로 지정한 파일에서 데이터를 읽어온다. #include <unistd.h> ssize_t read(int filedes, void *buf, size_t count); (ssize_t == int) filedes 읽기 작업을 수행할 파일에 대한 기술자이다. buf 파일로부터 읽어 들인 내용을 저장하기 위한 공간이다. 일반적으로 배열을 사용하게 되는데 배열의 데이터 형식은 어느 것이라도 상관없다. count 읽어 들일 파일 내용의 크기를 지정한다. 바이트 단위로 기술한다. 반환값 파일로부터 읽기 작업이 성공할 경우 1) 읽어 들인 파일 내용의 바이트 크기가 반환 (1 이상의 값) 2) 읽어 들인 내용이 없을 경우 (EOF일 경우) 0을 반환 읽기 작업이 실패할 경우 1) -1을 반환
read, write 06 write는 파일기술자로 지정한 파일로 데이터를 저장한다. #include <unistd.h> ssize_t write(int filedes, const void *buf, size_t count); filedes 쓰기 작업을 수행할 파일에 대한 기술자이다. buf 파일로 쓰려고 하는 내용이 저장되어 있는 공간이다. 일반적으로 배열을 사용하게 되면 배열의 데이터 형식은 어느 것이라도 상관없다. count buf에 있는 데이터 중에 실제로 파일로 저장할 데이터의 크기이다. 반환값 파일로 쓰기가 성공한 데이터의 크기이다. 대부분의 경우 count에서 지정한 값과 동일한 값이 반환된다. 만약 count의 값과 반환값이 다르다면 쓰기 작업이 실패한 것이다.
read, write 06 read/write의 성공 여부를 검사하는 예제 코드 if((nread = read(filedes, buf, BUFSIZE)) > 0) … read는 0 이상의 값을 반환할 때 호출이 성공한 것이다. read가 0을 반환하면 파일의 내용을 모두 읽었기 때문에 더 이상 읽을 것이 없음을 의미한다. write는 항상 세 번째 인수로 지정한 것과 같은 값이 반환되어야 호출이 성공한 것이다. 호출이 실패할 경우 반환값은 세 번째 인수의 값보다 작다. /* write가 정상적으로 수행되었는지 검사 */ if((write(filedes, buf, nread) < nread) …
【예제 3-4】ex03-02.c 02 02 temp1.txt 파일을 읽어서 temp2.txt로 복사한다. 01 #include <unistd.h> 02 #include <fcntl.h> 03 #include <stdlib.h> 04 int main() 05 { 06 int fdin, fdout; 07 ssize_t nread, nwrite; 08 char buffer[1024]; 09 10 fdin = open("temp1.txt", O_RDONLY); 11 fdout = open("temp2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
【예제 3-4】ex03-02.c 02 02 12 /* 정상적으로 읽어 들인 내용이 1바이트 이상인 동안 반복문 수행 */ 12 /* 정상적으로 읽어 들인 내용이 1바이트 이상인 동안 반복문 수행 */ 13 while( (nread = read(fdin, buffer, 1024)) > 0 ) 14 { 15 /* write 수행 */ 16 nwrite = write(fdout, buffer, nread); 17 18 /* write가 비정상적으로 수행되었다. (실패) */ 19 if ( nwrite < nread ) 20 exit(1); 21 } 22 23 close(fdin); 24 close(fdout); 25 } $ echo "System programming" > temp1.txt [schan@fc5vm 3.File_syscall]$ cat temp1.txt System programming [schan@fc5vm 3.File_syscall]$ cat temp2.txt cat: temp2.txt: No such file or directory [schan@fc5vm 3.File_syscall]$ ./a.out
lseek 07 지정한 파일에 대해서 읽기/쓰기 포인터의 위치를 변경한다. #include <sys/types.h> #include <unistd.h> off_t lseek(int filedes, off_t offset, int whence); ( off_t == int ) filedes 읽기/쓰기 포인터를 변경할 파일을 지정한다. offset 새롭게 지정할 읽기/쓰기 포인터의 위치를 의미한다. 기준점(whence)으로부터의 상대적 위치이다. 오프셋이기 때문에 기준에 따라 음수가 될 수도 있다. whence offset의 기준이 된다. 파일의 맨 처음(SEEK_SET), 현재 포인터의 위치(SEEK_CUR), 파일의 맨 마지막(SEEK_END) 등 세 가지가 있다. 반환값 작업이 성공하면 파일의 시작을 기준으로 한 포인터의 오프셋을 반환한다. 작업이 실패할 경우 -1이 반환된다.
lseek 07 세 번째 인수 whence SEEK_SET 파일의 첫 번째 바이트를 시작점으로 한다. SEEK_CUR offset의 기준 SEEK_SET 파일의 첫 번째 바이트를 시작점으로 한다. SEEK_CUR 읽기/쓰기 포인터의 현재 위치를 시작점으로 한다. SEEK_END 파일의 끝(end-of-file)을 시작점으로 한다.
lseek 07 lseek의 사용 예 off_t newpos; newpos = lseek(filedes, (off_t)0, SEEK_SET); 파일의 첫 번째 바이트로 읽기/쓰기 포인터를 옮긴다. newpos = lseek(filedes, (off_t)2, SEEK_CUR); 현재 위치에서 뒤로 2바이트만큼 옮긴다. newpos = lseek(filedes, (off_t)0, SEEK_END); 파일의 마지막 바이트 바로 뒤(EOF)로 옮긴다. newpos = lseek(filedes, (off_t)-1, SEEK_END); 파일의 마지막 바이트로 옮긴다. a b c d e f EOF
【예제 3-5】ex03-03.c 02 02 01 /* lseek을 사용하여 파일의 크기를 계산한다. */ 02 #include <sys/types.h> 03 #include <stdio.h> 04 #include <fcntl.h> 05 06 int main() 07 { 08 int filedes; 09 off_t newpos; 10 11 filedes = open(“temp1.txt", O_RDONLY); 12 13 /* 읽기/쓰기 포인터를 EOF로 이동한다. */ 14 newpos = lseek(filedes, (off_t)0, SEEK_END); 15 16 printf("file size : %d\n", newpos); 17 }
unlink, remove 08 경로명으로 지정한 파일을 삭제한다 #include <unistd.h> int unlink(const char *pathname); … #include <stdio.h> int remove(const char *pathname); pathname 삭제할 파일의 경로 이름이다. 반환값 작업이 성공할 경우 0이 반환되며, 실패할 경우 -1이 반환된다. pathname으로 지정한 파일을 삭제한다. 비어 있는 디렉터리는 remove만 삭제할 수 있다. (unlink는 불가능) ↔ 비어 있지 않은 디렉터리는 둘 모두 삭제할 수 없다.