UNIX file 접근 primitives
- file : byte들의 linear sequence
- 파일 입출력
- 저수준 파일 입출력 (open, close, read, write, dup, dup2, fcntl, lseek, fsync)
- 고수준 파일 입출력 (fopen, fclose, fread, fwrite, fputs, fgets, fprintf, fscanf, freopen, fseek)
file descriptor
≒ 파일 포인터
- 현재 open된 file을 구분할 목적으로 UNIX가 붙여 놓은 번호
- 표준 입출력
- 0 : 표준 입력
- 1 : 표준 출력
- 2 : 표준 오류 출력
- 한 프로세스가 동시에 open 할 수 있는 file의 개수에는 제한이 있음.
- close 사용 (close를 이용해 안 쓰는 파일은 닫아야함)
open 시스템 호출
- 기존의 file을 open 하거나, 새롭게 file을 생성하여 open 하는 system call
- 사용법 :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *filename, int oflag, [mode_t mode]);
//두 번째 인수에 따라서 세번째 인수를 사용할수도 있고 하지 않을수도 있음
- 인수 사용법
- filename : 파일 이름
- oflag : file을 access하는 방식
- O_RDONLY 또는 O_WRONLY 또는 O_RDWR
(파일이 존재했을 때 : 1. 파일을 읽기 2. 파일을 쓰기 가능 3. 파일 읽기, 쓰기 가능) - (만약 파일이 없으면 creat랑 파일 acces하는 방식, 총 두개를 사용)
그리고, file을 create하는 방식
O_CREAT 그리고 O_EXCL 또는 O_TRUNC
(O_EXCL : 만약 파일이 존재하지 않는 경우에는 파일을 새로 만들어서 오픈을 할 건데, 만약 파일이 존재한다면 파일을 오픈하지 않을 것임. -> 오픈 실패)
(O_TRUNC : 파일이 존재하면 기존에 있는 파일을 다 지우고 오픈) - 그리고, O_APPEND
(파일을 오픈하자마자 파일 포인터를 파일의 가장 뒤로 가게 하는 것. 기존의 파일에 이어서 쓰고 싶을 때 사용)
- O_RDONLY 또는 O_WRONLY 또는 O_RDWR
- mode : file을 새로 생성할때만 사용 (0600 또는 0664 또는 0644 또는 …)
(첫 번째 숫자 : 내가 파일에 대해 어떠한 권한을 갖고 있는지, 두번째 숫자 : 나랑 같은 그룹의 사람들이 저 파일에 대해 어떤 권한을 갖고 있는지, 세번 째 숫자 : 그밖의 사람들이 파일에 대해 어떤 권한을 가지고 있는지 지정해주는 숫자)
(읽기가 가능하게 설정하고 싶으면 4 + 쓰기가 가능하게 설정하고 싶으면 2 + 실행이 가능하게 설정하고 싶으면 1) - return 값 : 실행 성공 시 file descriptor (음이 아닌 정수); 실행 실패 시 -1
//맨처음 : 1-1파일은 없는 상태임. 1--2파일은 내용이 채워져있는 파일. 1-3파일 존재함.
int main(void) {
int fd;
//읽기만 가능하게 오픈
fd = open("data1-1", O_RDONLY);
printf("#1 %d\n", fd);
//1-1파일이 없으므로 명령 실패. 따라서 -1 출력
//쓰기 가능하게 오픈. 만약 파일이 없다면 만들어서 오픈하고 0600 커미션 붙이기
fd = open("data1-1", O_WRONLY | O_CREAT, 0600);
printf("#2 %d\n", fd);
//1-1파일 만들어서 오픈됨. 첫 번째 오픈된 파일이므로 3출력
//파일이 없으면 만들어서 오픈을 하고, 파일이 있으면 파일 오픈하지 않음
fd = open("data1-1", O_WRONLY | O_CREAT | O_EXCL, 0600);
printf("#3 %d\n", fd);
//1-1파일을 이미 만들었으므로, 실행 실패. 따라서 -1 출력
//파일을 읽기 쓰기 가능하게 오픈할건데, 파일이 없으면 만들어서 오픈
//파일이 있으면 기존의 내용을 싹 지우고 오픈
fd = open("data1-2", O_RDWR | O_CREAT | O_TRUNC, 0600);
printf("#4 %d\n", fd);
//1-2파일의 내용이 모두 지워진 상태로 오픈됨. 4 출력
//쓰기용으로 오픈하자마자 파일 포인터를 파일의 끝으로 옮김.
fd = open("data1-3", O_WRONLY | O_APPEND);
printf("#5 %d\n", fd);
//5 출력
return 0;
}
creat 시스템 호출
- file을 생성하여 open하거나, 기존 file을 open하는 system call
- 사용법
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char *filename, mode_t mode);
//첫 번째 인자 : 파일 이름, 두 번째 인자 : 모드
//파일이 있으면, 기존의 모드에 영향을 주지 않음.
- 주의 사항 :
- file을 쓰기 가능한 상태로 open
(무조건 WRONLY로 오픈) - file이 이미 존재하면: 두 번째 인자는 무시; 기존 file은 0으로 open!
(파일이 존재하면 파일의 내용을 다 지우고 오픈. open함수에서 O_CREAT|O_TRUNC 라고 봐도 무방)
- file을 쓰기 가능한 상태로 open
close 시스템 호출
- open 된 file을 close 할 때 사용
- process 종료 시 open된 file들은 자동으로 close.
- 그러나, 동시에 open할 수 있는 file 수 제한 때문에 close 사용
- 사용법 :
#include <unistd.h>
int close(int filedes);
- 인수 사용법 :
- filedes : open된 file의 file descriptor
- return 값 : 성공 시 0; 실패 시 -1
read 시스템 호출
- open된 file로부터 지정한 byte수 만큼의 data를 읽어 지정된 저장장소에 저장하는 명령
- file pointer or read-write pointer : 읽혀질 다음 byte의 위치를 나타냄
- 사용법 :
#include <unistd.h>
ssize_t read(int filedes, void *buffer, size_t nbytes);
//첫 번째 인자 : 파일의 번호
//두 번째 인자 : 파일에서 데이터를 읽어서 저장할 변수의 주소. 타입 상관 X (정수, 문자, 구조체..)
//세 번째 인자 : 바이트 숫자.
- 인수 사용법 :
- filedes : open된 file의 file descriptor
- *buffer : 읽은 data를 저장할 곳의 주소; data type은 상관 없음
- nbytes : 읽을 byte 수; data type에 상관 없이 지정된 byte 수 만큼 읽음
- return 값 : 성공 시, 실제 읽힌 byte 수; 실패 시, -1
- return 값 < nbytes이면, file에 남은 data가 nbytes보다 적을 때
- 그 다음은 더 이상 읽을게 없으면; return 값은 0
(read를 할때마다 파일 포인터가 계속 움직임.)
(읽기 실패 : file descriptor를 잘못 적었을때 즉, 파일이 없을때. 저장할 곳의 주소를 잘 못 적었을때. 저장할 곳이 없으면 실패)
//data2 파일 존재
int main(void) {
char ch[100];
int fd, n;
fd = open("data2", O_RDONLY);
n = read(fd, ch, 99); //99글자 읽기
ch[n] = '\0';
printf("fd=%d n=%d ch=%s", fd, n, ch);
return 0;
}
//data3 파일 존재
int main(void) {
int in[5], fd, i;
fd = open("data3", O_RDONLY);
for (i = 0; i < 5; i++) {
read(fd, in + i, sizeof(int));
printf("%d\n", in[i]);
}
return 0;
}
//data3존재함. 이 코드가 위보다 더 잘 짠 코드
int main(void) {
int in[5], fd, i;
fd = open("data3", O_RDONLY);
read(fd, in, sizeof(int) * 5);
//한 번에 정수 다섯 개 읽음.
for (i = 0; i < 5; i++) {
printf("%d\n", in[i]);
}
return 0;
}
write 시스템 호출
- 지정된 저장장소에서 지정한 byte수 만큼의 data를 읽어 open된 file에 쓰는 명령
- file pointer or read-write pointer: 쓰여질 다음 byte의 위치를 나타냄
- 사용법 :
#include <unistd.h>
ssize_t write(int filedes, const void *buffer, size_t nbytes);
- 인수 사용법 :
- filedes : write를 할 file의 descriptor
- *buffer : write 할 내용이 들어 있는 저장 장소의 주소
- nbytes : write할 byte의 수
- return 값 : 쓰여진 byte 수 or -1
- 보통은 return값 = n
- return 값 < n이면, 쓰는 도중 media가 full
- 만약, 쓰기 전에 꽉 차면 -1 return
- 주의 사항
- 기존 file을 open system call로 open하고 바로 write를 하면?
(파일 포인터는 맨 처음으로 가게 됨. 따라서 바로 write를 하면 덮어쓰게 됨)
(이를 원하지 않으면) - fd = open(“data”, O_WRONLY|O_APPEND);
→ open 하자마자, file pointer가 file의 끝으로 이동;
- 기존 file을 open system call로 open하고 바로 write를 하면?
//data4는 존재하지 않음
int main(void) {
char ch[100] = {};
int fd, i;
fd = open("data4", O_WRONLY | O_CREAT, 0600);
for (i = 0; i < 3; i++) {
scanf("%s", ch);
write(fd, ch, strlen(ch));
}
return 0;
}
//data5는 존재하지 않음 -> 새로 만들 것임
int main(void) {
int in[5], fd, i;
fd = open("data5", O_WRONLY | O_CREAT, 0600);
for (i = 0; i < 3; i++) {
scanf("%d", &in[i]);
write(fd, in + i, sizeof(int));
}
return 0;
}
//한꺼번에 write하는 방식
int main(void) {
int in[5], fd, i;
fd = open("data5", O_WRONLY | O_CREAT, 0600);
for (i = 0; i < 3; i++) {
scanf("%d", &in[i]);
}
write(fd, in, sizeof(int) * 5);
return 0;
}
read/write의 효율성
- File을 copy하는 프로그램의 실행 시간
- BUFSIZE가 512 (disk blocking factor)의 배수 일 때, 효율적;
- system call의 수가 적을수록 효율적
'전공 > 유닉스' 카테고리의 다른 글
6장. 프로세스 생성과 실행 (1) | 2023.11.01 |
---|---|
5장. 프로세스 정보 (0) | 2023.11.01 |
4장. 시스템 정보 (0) | 2023.11.01 |
3장. 파일과 디렉토리 (1) | 2023.11.01 |
2장. 파일 입출력 (2) (1) | 2023.10.23 |
댓글