0w0
저수준 파일 입출력 본문
저수준 파일 입출력
저수준 파일 입출력은 유닉스 커널의 시스템 호출을 사용하여 파일 입출력을 수행한다.
파일 지시자 : int fd (파일 기술자)
특징 : 훨씬 빠르다.
바이트 단위로 읽고 쓴다.
특수 파일에 대한 접근이 가능하다.
파일 열기 : open(2)
open 함수는 파일 열기에 성공하면 파일 기술자를 리턴한다. 파일 열기에 실패하면 -1을 리턴하고, 외부 변수 errno에 실패한 이유를 설명하는 오류 코드를 저장한다. 이 오류 코드를 perror 함수로 출력하면 해당 메시지를 확인할 수 있다.
함수원형
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h> //file control의 약자
int open(const char *path, int oflag [, mode_t mod]);
//path : 열려는 파일이 있는 경로
//oflag : 파일 상태 플래그
//mod : 접근권한
O_RDONLY | 파일을 읽기 전용으로 연다 | 0x0001 |
O_WRONLY | 파일을 쓰기 전용으로 연다 | 0x0002 |
O_RDWR | 파일을 읽기/쓰기용으로 연다 | 0x0004 |
O_CREAT | 파일이 없으면 생성, 파일을 생성할 권한은 당연히 있어야 한다. | 0x0100 |
O_TRUNC | 파일을 생성할 때 이미 있는 파일이고, 쓰기 옵션으로 열었으면 내용을 모두 지우고 파일의 길이를 0으로 변경한다. | 0x0200 |
O_EXCL | O_CREAT과 함께 사용할 경우 기존에 없는 파일이면 생성하지만, 이미 있으면 파일을 생성하지 않고 오류 메시지를 출력한다.ㅣ | 0x0400 |
O_APPEND | 이 옵션을 지정하면 파일의 맨 끝에 내용을 추가한다. | 0x0800 |
O_TEXT | 화일을 텍스트 형식으로 연다. | 0x4000 |
O_BINARY | 화일을 이진 형식으로 연다. | 0x8000 |
mode
mode는 파일의 접근 권한을 설정하는 것으로, O_CREAT 플래그를 지정해 파일을 생성할 때만 사용한다.
S_IRWXU 00700 user (file owner) has read, write, and execute permission
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others have read, write, and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
According to POSIX, the effect when other bits are set in mode is unspecified. On
Linux, the following bits are also honored in mode:
S_ISUID 0004000 set-user-ID bit
S_ISGID 0002000 set-group-ID bit (see stat(2))
S_ISVTX 0001000 sticky bit (see stat(2))
파일 생성 : creat(2)
creat 함수로 파일을 생성하면 파일 기술자가 리턴되므로, 별도로 open 함수를 호출해 파일을 열 필요가 없다.
creat 함수는 open 함수와 달리 옵션을 지정하는 부분이 없다.
함수원형
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h> //file control의 약자
int creat(const char *path, mode_t mode);
//path : 파일을 생성할 경로
//mod : 접근 권한
파일 닫기 : close(2)
creat 함수로 파일을 생성하면 파일 기술자가 리턴되므로, 별도로 open 함수를 호출해 파일을 열 필요가 없다.
creat 함수는 open 함수와 달리 옵션을 지정하는 부분이 없다.
함수원형
#include<unistd.h>
int close(int fildes);
//fildes : 파일 기술자
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd;
mode_t mode;
mode=S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
//파일의 접근 권한을 OR연산으로 지정한다.
fd=open("unix.txt", O_CREAT, mode);
//unix.txt파일을 12행에서 지정한 권한(0644)으로 생성
if(fd==-1){
perror("Creat");
exit(1);
}
close(fd);
return 0;
}
결과!!
[3210w0@localhost ex2_1_directory]$ ./ex2_1
[3210w0@localhost ex2_1_directory]$ ll
합계 16
-rwxrwxr-x. 1 3210w0 3210w0 8784 10월 6 22:02 ex2_1
-rw-rw-r--. 1 3210w0 3210w0 529 10월 6 22:02 ex2_1.c
-rw-r--r--. 1 3210w0 3210w0 0 10월 6 22:02 unix.txt
[3210w0@localhost ex2_1_directory]$
//O_EXCL 플래그 사용하기
//O_EXCL 플래그 사용하기
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd;
fd=open("unix.txt", O_CREAT | O_EXCL, 0644);
if(fd==-1){
perror("Creat");
exit(1);
}
close(fd);
return 0;
}
결과!!
[3210w0@localhost ex2_2_directory]$ gcc -o ex2_2 ex2_2.c
[3210w0@localhost ex2_2_directory]$ ll
합계 16
-rwxrwxr-x. 1 3210w0 3210w0 8784 10월 6 22:07 ex2_2
-rw-rw-r--. 1 3210w0 3210w0 382 10월 6 22:07 ex2_2.c
[3210w0@localhost ex2_2_directory]$ ./ex2_2
[3210w0@localhost ex2_2_directory]$ ll
합계 16
-rwxrwxr-x. 1 3210w0 3210w0 8784 10월 6 22:07 ex2_2
-rw-rw-r--. 1 3ㅋ210w0 3210w0 382 10월 6 22:07 ex2_2.c
-rw-r--r--. 1 3210w0 3210w0 0 10월 6 22:07 unix.txt
[3210w0@localhost ex2_2_directory]$ ./ex2_2
Creat: File exists
[3210w0@localhost ex2_2_directory]$ rm unix.txt
[3210w0@localhost ex2_2_directory]$ ./ex2_2
[3210w0@localhost ex2_2_directory]$ ll
합계 16
-rwxrwxr-x. 1 3210w0 3210w0 8784 10월 6 22:07 ex2_2
-rw-rw-r--. 1 3210w0 3210w0 382 10월 6 22:07 ex2_2.c
-rw-r--r--. 1 3210w0 3210w0 0 10월 6 22:08 unix.txt
[3210w0@localhost ex2_2_directory]$
파일 기술자 할당하기
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd;
close(0);
//0번 파일 기술자를 닫는다.
fd=open("unix.txt", O_RDWR);
//남아 있는 파일 기술자 중 가장 작은 숫자가 0이므로 unix.txt파일을 가리키는 파일 기술자는 0이 된다.
if(fd==-1){
perror("Excl");
exit(1);
}
printf("unix.txt : fd=%d\n",fd);
close(fd);
return 0;
}
결과!!
3210w0@localhost ex2_3_directory]$ ./ex2_3
unix.txt : fd=0
[3210w0@localhost ex2_3_directory]$ ll
total 16
-rwxrwxr-x. 1 3210w0 3210w0 8832 Oct 11 11:53 ex2_3
-rw-rw-r--. 1 3210w0 3210w0 513 Oct 11 11:53 ex2_3.c
-rw-rw-r--. 1 3210w0 3210w0 0 Oct 11 11:54 unix.txt
[3210w0@localhost ex2_3_directory]$
파일 읽기 : read(2)
read와 write 함수의 리턴값의 데이터형인 ssize_t는 <sys/types.h> 파일에 int(환경에 따라 long)로 정의되어 있다.
함수원형
#include<unistd.h>
ssize_t read(int fildes, void *buf, size_t nbytes);
//filders : 파일 기술자
//buf : 바이트를 저장할 메모리 영역의 시작 주소
//nbytes : 읽어올 바이트 수
//파일 읽기
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd,n;
char buf[10];
fd=open("unix.txt", O_RDONLY);
//unix.txt파일을 읽기 전용으로 연다.
if(fd==-1){
perror("Open");
exit(1);
}
n=read(fd,buf,6);
//6바이트를 읽어 buf에 저장하고 read함수의 리턴값을 저장한다.
//(출력 결과를 보면 6임을 알 수 있다.)
if(n==-1){
perror("Read");
exit(1);
}
buf[n]='\0';
//read 함수는 읽어온 데이터의 끝에 자동으로 널을 추가하지 않기 때문에 buf에 널 문자('\0')을 추가한다.
//buf를 문자열로 출력하려면 널을 추가해야 한다.
printf("n=%d, buf=%s\n",n,buf);
//unix.txt 파일에서 읽어온 바이트 수와 buf의 내용을 출력한다.
//(실행 결과를 보면 unix.txt 파일의 처음 여섯 바이트를 읽어왔음을 알 수 있다.)
close(fd);
return 0;
}
결과!!
[3210w0@localhost ex2_4_directory]$ gcc -o ex2_4 ex2_4.c
[3210w0@localhost ex2_4_directory]$ ./ex2_4
Open: No such file or directory
[3210w0@localhost ex2_4_directory]$ cat > unix.txt
asdf
^C
[3210w0@localhost ex2_4_directory]$
[3210w0@localhost ex2_4_directory]$ ./ex2_4
n=5, buf=asdf
파일 쓰기 : write(2)
read와 write 함수의 리턴값의 데이터형인 ssize_t는 <sys/types.h> 파일에 int(환경에 따라 long)로 정의되어 있다.
함수원형
#include<unistd.h>
ssize_t write(int fildoes, const void *buf, size_t nbytes);
//filders : 파일 기술자
//buf : 파일에 기록할 데이터를 저장한 메모리 영역
//nbytes : buf의 크기(기록할 데이터의 적기)
//파일 읽고 쓰기 ex2_5.c
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(){
int rfd, wfd, n;
char buf[10];
rfd=open("unix.txt", O_RDONLY);
if(rfd==-1){
perror("Open unix.txt");
exit(1);
}
wfd=open("unix.bak", O_CREAT | O_WRONLY | O_TRUNC, 0644);
//unix.bak 파일을 쓰기 전용으로 생성하며, 기존 파일이 있을 경우 내용을 비운다.
if(wfd==-1){
perror("Open unix.bak");
exit(1);
}
while((n=read(rfd, buf, 6))>0) //read 함수가 리턴한 값이 buf에 저장된 데이터의 크기므로,
if(write(wfd,buf,n)!=n) //이를 23행에서 write 함수의 세번째 인자로 사용할 수 있다.
perror("Write"); //write 함수의 리턴값이 출력할 데이터의 크기인 n과 다르면 쓰기 동작에 문제가 있다는 의미
//while 문을 사용해 unix.txt 파일을 6 바이트씩 읽고 출력한다.
if(n==-1)
perror("Read");
close(rfd);
close(wfd);
return 0;
}
//실행 결과 unix,.bak 파일이 생성되고 데이터가 저장되었다. 즉, unix.txt 파일이 unix.bak 파일로 복사된 것이다.
결과!!
[3210w0@localhost ex2_5_directory]$ ll
total 16
-rwxrwxr-x. 1 3210w0 3210w0 8880 Oct 7 10:23 ex2_5
-rw-rw-r--. 1 3210w0 3210w0 482 Oct 7 10:32 ex2_5.c
[3210w0@localhost ex2_5_directory]$ ./ex2_5
Open unix.txt: No such file or directory
[3210w0@localhost ex2_5_directory]$ gcc -o ./ex2_5 ./ex2_5.c
[3210w0@localhost ex2_5_directory]$ ll
total 16
-rwxrwxr-x. 1 3210w0 3210w0 8880 Oct 7 10:35 ex2_5
-rw-rw-r--. 1 3210w0 3210w0 486 Oct 7 10:35 ex2_5.c
[3210w0@localhost ex2_5_directory]$ cat > unix.txt
asdf
^C
[3210w0@localhost ex2_5_directory]$
[3210w0@localhost ex2_5_directory]$ ./ex2_5
[3210w0@localhost ex2_5_directory]$ ll
total 24
-rwxrwxr-x. 1 3210w0 3210w0 8880 Oct 7 10:35 ex2_5
-rw-rw-r--. 1 3210w0 3210w0 486 Oct 7 10:35 ex2_5.c
-rw-r--r--. 1 3210w0 3210w0 5 Oct 7 10:35 unix.bak
-rw-rw-r--. 1 3210w0 3210w0 5 Oct 7 10:35 unix.txt
파일 오프셋 위치 지정 : lseek(2)
파일의 내용을 읽거나 쓰면 현재 읽을 위치나 쓸 위치를 알려주는 오프셋(offeset)이 자동으로 변경된다.
한 파일에서 파일 오프셋은 오직 하나다. 다시 말해, 파일을 읽기/쓰기 모드로 열었을 때 파일 읽기 오프셋과 쓰기 오프셋이 별도로 있는 것이 아니므로 주의해야 한다.
오프셋을 원하는 위치로 바꾸고, 위치를 확인하려면 lseek 함수를 사용한다.
함수원형
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fildes, off_t offset, int whence);
//fildes : 파일 기술자
//offset : 이동할 오프셋 위치
//whence : 오프셋의 기준 위치
값 |
설명 |
SEEK_SET |
파일의 시작 기준 |
SEEK_CUR |
현재 위치 기준 |
SEEK_END |
파일의 끝 기준 |
ex)
lseek(fd, 5, SEEK_SET) (파일의 시작에서 5번째 위치로 이동)
lseek(fd, 0, SEEK_END) (파일의 끝에서 0번째므로 파일의 끝으로 이동)
//파일 오프셋 사용하기
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd, n;
off_t start, cur;
char buf[256];
fd=open("unix.txt", O_RDONLY);
//unix.txt 파일을 읽기 전용으로 연다.
if(fd==-1){
perror("Open unix.txt");
exit(1);
}
start=lseek(fd, 0, SEEK_CUR);
//파일의 현재 위치를 파악해보면 오프셋이 0임을 알 수 있다.
n=read(fd, buf, 255);
buf[n]='\0';
printf("Offset start=%d, Read Str=%s, n=%d\n", (int)start, buf, n);
cur=lseek(fd, 0, SEEK_CUR);
//데이터를 읽은 후 현재 위치를 확인한다(이 예제에서는 오프셋이 start+n 위치로 이동한다.)
printf("Offset cur=%d\n", (int)cur);
start=lseek(fd, 5, SEEK_SET);
n=read(fd, buf, 255);
//파일의 시작 기준으로 오프셋이 5인 위치로 이동한 후 데이터를 읽는다.
buf[n]='\0';
printf("Offset start=%d, Read Str=%s",(int)start, buf);
close(fd);
return 0;
}
//실행 결과를 보면 오프셋의 위치에 따라 읽어온 데이터가 다름을 알 수 있다.
결과!!
[3210w0@localhost ex2_6_directory]$ gcc -o ex2_6 ex2_6.c
l[3210w0@localhost ex2_6_directory]$ ll
합계 16
-rwxrwxr-x. 1 3210w0 3210w0 8936 10월 8 07:09 ex2_6
-rw-rw-r--. 1 3210w0 3210w0 1067 10월 8 07:09 ex2_6.c
[3210w0@localhost ex2_6_directory]$ ./ex2_6
Open unix.txt: No such file or directory
[3210w0@localhost ex2_6_directory]$ cat > unix.txt
Unix ~~
^C
[3210w0@localhost ex2_6_directory]$
[3210w0@localhost ex2_6_directory]$ ./ex2_6
Offset start=0, Read Str=Unix ~~
, n=8
Offset cur=8
Offset start=5, Read Str=~~
파일 기술자 복사하기 : dup(2)
파일을 열려면 파일 기술자가 할당된다.
이 파일 기술자를 복사해 같은 파일을 가리키는 두 번째 파일 기술자를 생성할 수 있다.
오프셋을 원하는 위치로 바꾸고, 위치를 확인하려면 lseek 함수를 사용한다.
함수원형
#include<unistd.h>
int dup(int fildes);
//fildes : 파일 기술자
//파일 기술자 복사하기
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd, fd1;
fd=open("tmp.aaa", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if(fd==-1){
perror("Create tmp.aaa");
exit(1);
}
close(1);
//1번 파일 기술자(표준 출력)를 닫는다.
fd1=dup(fd);
/*dup(fd)를 실행하면 파일 기술자 fd가 가리키는 파일에 새로운 파일 기술자가 지정된다.
15행에서 1번을 닫았기 때문에 현재 가장 작은 값은 1이고, 이 값이 새로운 파일 기술자로 할당된다.*/
printf("DUP FD=%d\n", fd1);
printf("Standard Output Redirection\n");
//현재 1번 파일 기술자가 가리키는 파일인 tmp.aaa로 출력된다.
close(fd);
return 0;
}
결과!!
[3210w0@localhost ex2_7_directory]$ ll
합계 20
-rwxrwxr-x. 1 3210w0 3210w0 8928 10월 8 07:58 ex2_7
-rw-rw-r--. 1 3210w0 3210w0 737 10월 8 07:58 ex2_7.c
-rw-r--r--. 1 3210w0 3210w0 37 10월 8 07:58 tmp.aaa
[3210w0@localhost ex2_7_directory]$ cat tmp.aaa
DUP FD=1
Standard Output Redirection
파일 기술자 복사하기 : dup2(3)
dup2 함수는 새로운 파일 기술자를 지정할 수 있게 해준다.
dup2 함수는 파일 기술자 fildes를 fildes2로 복사한다.
함수원형
#include<unistd.h>
int dup2(int fildes, int fildes2);
//fildes : 파일 기술자
//fildes2 : 파일 기술자를 복사할 곳
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd;
fd=open("tmp.bbb", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if(fd==-1){
perror("Create tmp.bbb");
exit(1);
}
dup2(fd, 1);
/*dup2 함수를 사용해 기존 파일 기술자(fd)를 1번 파일 기술자로 복사한다. 이제 1번 파일 기술자로 출력하면 더이상 표준 출력(화면)이 아닌 fd가 가리키는 파일로 출력한다.*/
printf("DUP2 : Standard Output Redirection\n ");
close(fd);
return 0;
}
결과!!
[3210w0@localhost ex2_8_directory]$ ll
합계 20
-rwxrwxr-x. 1 3210w0 3210w0 8880 10월 8 08:07 ex2_8
-rw-rw-r--. 1 3210w0 3210w0 538 10월 8 08:07 ex2_8.c
-rw-r--r--. 1 3210w0 3210w0 36 10월 8 08:07 tmp.bbb
파일 기술자 복사하기 : fcntl(2)
fcntl 함수를 사용하면 파일을 열 때 설정한 플래그들을 조정할 수 있다.
함수원형
#Include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fildes, int cmd, /* arg*/ ...);
//fildes : 파일 기술자
//cmd : 명령
//arg : cmd에 따라 필요시 지정하는 인자들
F_GETFL |
상태 플래그 정보를 읽어온다. |
F_SETFL |
상태 플래그 정보를 설정한다. 설정할 수 있는 플래그는 대부분 openㅏㅁ수에서 지정하는 플래그다. |
//fcntl 함수로 파일 기술자 제어하기
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int main(void){
int fd,flags;
fd=open("unix.txt", O_RDWR);
//unix.txt 파일을 읽기/쓰기용으로 연다.
if(fd==-1){
perror("open");
exit(1);
}
if((flags=fcntl(fd, F_GETFL))==-1){
//F_GETFL 명령을 설정해 fcntl 함수를 호출하면 현재 설정된 플래그 값이 리턴된다.
perror("fcntl");
exit(1);
}
flags |=O_APPEND;
//리턴된 플래그 값에 변경할 플래그를 OR로 연결해 새로운 플래그로 저장한다.
if(fcntl(fd, F_SETFL, flags)==-1){
//저장된 플래그를 F_SETFL명령으로 설정해 fcntl 함수를 호출하면
//기존 플래그가 새로운 플래그로 변경된다.
perror("fcntl");
exit(1);
}
if(write(fd, "Hanbit Media", 12)!=12) perror("write");
//파일의 설정을 O_APPEND로 변경했기 때문에 파일에 출력하면 내용이 파일의 끝부분에 추가된다.
close(fd);
return 0;
}
결과!!
[3210w0@localhost ex2_9_directory]$ cat > unix.txt
^C
[3210w0@localhost ex2_9_directory]$
[3210w0@localhost ex2_9_directory]$ ./ex2_9
[3210w0@localhost ex2_9_directory]$ cat unix.txt
Hanbit Media[3210w0@localhost ex2_9_directory]$
[3210w0@localhost ex2_9_directory]$
파일 삭제 : unlink(2)
파일을 삭제하려면 unlink(2)함수를 사용한다.
함수원형
#include<unistd.h>
int unlink(const char *path);
//path : 삭제할 파일의 경로
//unlink 함수로 파일 삭제하기
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
int cnt;
cnt=unlink("tmp.aaa");
//unlink 함수를 이용해 tmp.aaa 파일을 삭제한다.
if(cnt==-1){
perror("Unlink tmp.aaa");
exit(1);
}
printf("Unlink tmp.aaa success!!!\n");
//파일을 성공적으로 삭제하며 문자열을 메시지로 출력한다.
return 0;
}
결과!!
[3210w0@localhost ex2_10_directory]$ cat > tmp.aaa
^C
[3210w0@localhost ex2_10_directory]$
[3210w0@localhost ex2_10_directory]$ ./ex2_10
Unlink tmp.aaa success!!!
[3210w0@localhost ex2_10_directory]$ ll
합계 16
-rwxrwxr-x. 1 3210w0 3210w0 8784 10월 10 07:22 ex2_10
-rw-rw-r--. 1 3210w0 3210w0 362 10월 10 07:22 ex2_10.c
[3210w0@localhost ex2_10_directory]$
파일과 디스크 동기화 함수 : fsync(3)
파일을 삭제하려면 unlink(2)함수를 사용한다.
함수원형
#include<unistd.h>
int fsync( int fildes);
//fildes : 파일 기술자
'Coding > System Programming' 카테고리의 다른 글
고수준 파일 입출력 (0) | 2016.10.11 |
---|---|
명령행 인자 출력 ex1_6.c ex1_7.c (0) | 2016.09.28 |
동적 메모리 할당 malloc realloc (0) | 2016.09.28 |
오류 처리 함수 perror함수, str-error함수 (0) | 2016.09.27 |
Makefile과 make ex1_3_main.c ex1_3_addnum.c (0) | 2016.09.27 |