- 실행 결과
<img width="402" alt="image" src="https://user-images.githubusercontent.com/86760744/179477027-91e662de-e210-42ba-ad09-839e37d3e0ed.png">
<br><br>
## 파일 생성 및 관리 (stat)
``` c
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
리눅스는 파일의 메타 데이터 정보를 얻기 위해 stat()을 제공한다.
stat 구조체에 정보를 저장하며 stat 구조체는 <bits/stat.h>에 선언되어 있고 이는 <sys/stat.h>에 포함되어 있다.
struct stat {
dev_t st_dev; /* 파일을 포함하고 있는 장치 ID */
ino_t st_ino; /* inode 번호 */
mode_t st_mode; /* 권한 (permissions) */
nlink_t st_nlink; /* 하드 링크 수 */
uid_t st_uid; /* user ID */
gid_t st_gid; /* group ID */
dev_t st_rdev; /* device ID (특수 파일일 경우) */
off_t st_size; /* 바이트 단위의 전체 사이즈 */
blksize_t st_blksize; /* filesystem I/O 를 위한 블록 사이즈 */
blkcnt_t st_blocks; /* 할당된 블록의 개수 */
time_t st_atime; /* 마지막 접근 시간 */
time_t st_mtime; /* 마지막 편집 시간 */
time_t st_ctime; /* 마지막 상태 변경 시간 */
};
int main(int argc, char *argv[])
{
struct stat sb;
int ret;
if (argc < 2) {
printf("Usage: %s <file>\n", argv[0]);
return -1;
}
ret = stat(argv[1], &sb);
if (ret) {
perror("stat error: ");
return -1;
}
printf("file(%s) is %ld bytes\n", argv[1], sb.st_size);
return 0;
}
- 예시에서는 sb.st_size 를 가져왔으나, userId, groupId 등 다양한 정보를 가져올 수 있다.
<img width="486" alt="image" src="https://user-images.githubusercontent.com/86760744/179478815-61134138-b1b6-4a1c-be5a-06c5251b8d26.png">
<br><br>
## 권한(Permission) (chmod)
``` c
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
<br>
- 위의 `creat_example` 파일의 권한이 변경된 것을 확인할 수 있다.
![image](https://user-images.githubusercontent.com/86760744/179722528-9cd89985-48a5-4c6a-a707-0e1d6053fd2d.png)
<br><br>
## 소유권(Ownership) (chown)
``` c
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
경로 이름으로 지정된 파일의 소유권을 변경합니다.
정상 수행 시 파일 0 리턴, 에러 발생 시 -1 리턴 (errno)
소유권(Ownership) (chown) 의 예시
아래는 chown_example.c의 코드이다.
#include <stdio.h>
#include <unistd.h>
int main()
{
char *filename = "./creat_example";
int mode = F_OK;
- 실행 결과
![image](https://user-images.githubusercontent.com/86760744/182078157-1c4cf809-e85e-4c39-86dc-ca1a880b7132.png)
<br><br>
## 파일 열고 닫기 (open)
``` c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open은 시스템 호출로 파일을 열거나 생성할 때 사용한다.
성공하면 해당 파일을 지시하는 int 형의 파일 디스크립터를 되돌려준다.
path_name은 생성하거나 열고자 하는 파일 이름을 나타낸다.
보통 full path 이름을 적어주며, 단지 파일 이름만 적을 경우에는 현재 경로에서 찾는다.
flag는 파일을 어떤 모드로 열 것인지 결장하기 위해 사용한다.
읽기전용 : O_RDONLY
쓰기전용 : O_WRONLY
읽기/쓰기 : O_RDWR
open은 mode 인자가 붙은 형식과 붙지 않은 형식 둘 다 유효하다.
mode 인자는 파일을 생성 시, 파일의 권한(소유권)을 나타낸다.
파일을 생성(O_CREAT)하지 않으면 mode 인자는 무시된다.
반대로 O_CREAT로 파일을 생성 시, 파일 권한이 정의되지 않으면 종종 골치 아픈 일을 겪을 수 있다.
정상 수행 시, 파일 디스크립터를 반환하고, 에러 발생 시, -1을 반환한다. (errno)
flags
O_CREAT : 만약 pathname 파일이 존재하지 않을 경우 파일을 생성한다.
O_EXCL : O_CREAT 를 이용해서 파일을 생성하고자 할 때, 이미 파일이 존재한다면 에러발생
O_APPEND : 파일이 추가모드로 열린다. 파일의 위치는 파일의 끝이 된다.
O_NONBOLOCK, O_NDELAY : 파일이 비봉쇄(Nonblock) 모드로 열린다.
O_NOFOLLOW : 경로명이 심볼릭링크라면, 파일열기에 실패한다.
O_DIRECTORY : 경로명이 디렉토리가 아니라면 파일열기에 실패한다.
O_SYNC : 입출력 동기화 모드로 열린다. 모든 write 는 데이터가 물리적인 하드웨어에 기록될 때까지 호출 프로세스를 블록한다.
mode
S_IRWXU : 00700 모드로 파일 소유자에게 읽기, 쓰기, 쓰기 실행권한을 준다.
S_IRUSR : 00400 으로 사용자에게 읽기 권한을 준다.
S_IWUSR : 00200 으로 사용자에게 쓰기 권한을 준다.
S_IXUSR : 00100 으로 사용자에게 실행 권한을 준다.
S_IRWXG : 00070 으로 그룹에게 읽기, 쓰기, 실행 권한을 준다.
S_IRGRP : 00040 으로 그룹에게 읽기권한을 준다.
S_IWGRP : 00020 으로 그룹에게 쓰기권한을 준다.
S_IXGRP : 00010 으로 그룹에게 실행권한을 준다.
S_IRWXO : 00007 으로 기타 사용자 에게 읽기, 쓰기, 실행 권한을 준다.
S_IROTH : 00004 으로 기타 사용자 에게 읽기 권한을 준다.
S_IWOTH : 00002 으로 기타 사용자 에게 쓰기 권한을 준다.
S_IXOTH : 00001 으로 기타 사용자 에게 실행 권한을 준다.
errno
EEXIST : O_CREAT 와 O_EXECL 이 같이 사용되었을 경우 발생한다. 이미 경로파일이 존재할 경우 발생된다.
EACCES : 파일 접근이 거부될 경우이다. 주로 권한 문제 때문에 발생한다.
ENOENT : 경로명의 디렉토리가 없거나, 심볼릭 링크가 깨져있을 때.
ENOENT : 경로명의 디렉토리가 없거나, 심볼릭 링크가 깨져있을 때.
ENODEV : 경로명이 장치파일을 참고하고, 일치하는 장치가 없을 때.
EROFS : 경로명이 read-only 파일시스템을 참조하면서, 쓰기로 열려고 할 때.
EROFS : 경로명이 read-only 파일시스템을 참조하면서, 쓰기로 열려고 할 때.
EFAULT : 경로명이 접근할 수 없는 주소강간을 가리킬 때
ELOOP : 심볼릭 링크가 너무 많을 때.
파일 열고 닫기 (close)
#include <unistd.h>
int close(int fd);
close()는 파일 디스크립터를 닫아서 더 이상 파일을 참조하지 않고 재사용할 수 있도록 한다.
프로세스와 연관되고 프로세스가 소유한 파일에 보유된 모든 레코드 잠금이 제거된다.
fd가 열린 마지막 파일 디스크립터인 경우, 연관된 자원이 해제된다.
파일 디스크립터가 unlink를 사용해 제거된 파일에 대한 마지막 참조인 경우, 파일이 삭제된다.
정상 수행 시, 파일 디스크립터를 반환하고, 에러 발생 시 -1을 반환한다. (errno)
errno
EBADF : fd가 유효한 파일 디스크립터가 아닌 경우
EINTR : 시그널 호출에 의해 인터립트된 경우
EIO : I/O 에러 발생된 경우
ENOSPC, SDQUOT : NFS에서 스토리지 공간 초과 시 write, fsync, close에 대해 보고
파일 열고 닫기 (fcntl)
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */);
fcntl()은 파일 디스크립터를 조작한다.
fcntl()은 열린 파일 디스크립터 fd에 대해 아래 설명된 작업 중 하나를 수행한다.
작업은 cmd에 의해 결정된다.
선택적으로 세 번째 인수를 취할 수 있으며, 필요 여부는 cmd에 의해 결정된다.
인수가 필요하지 않은 경우 void 지정
CMD
파일 디스크립터 복제
F_DUPFD (int) : 복사된 파일 디스크립터는 잠금, 파일위치 포인터, 플래그 등을 공유한다.
lseek 등으로 위치 변경 시 복제된 모든 파일 디스크립터도 변경된다.
파일 상태 플래그
F_GETFL (void) : 파일 디스크립터에 대한 플래그 값(open 호출 시 지정한 플래그)을 되돌려 준다.
F_SETFL (int) : arg에 지정된 값으로 파일 디스크럽터 fd의 플래그를 재설정한다.
현재는 단지 O_APPEND, O_ASYNC, O_DIRECT, O_NONBLOCK 만을 설정할 수 있다.
다른 플래그들 (파일 엑세스 플래그 O_WRONLY와 같은, 파일 생성 플래그 O_CREAT와 같은)은 영향을 받지 않는다.
레코드 잠금
F_SETLK (struct flock *) : 잠금을 획득하거나 잠금을 풀기 위해서 사용
F_SETLKW (struct flock *) : F_SETLK와 같은 일을 하지만, 에러를 반환하는 대신, 잠금이 풀릴 때까지 대기한다. (block)
F_GETLK (struct flock *) : 잠금이 있는지 없는지 검사한다.
struct flock {
short int l_type /* 잠김 타입: F_RDLCK, F_WRLCK, or F_UNLCK. */
short int l_whence; /* 파일의 절대적 위치 */
__off_t l_start; /* 파일의 offset */
__off_t l_len; /* 잠그고자 하는 파일의 길이 */
__pid_t l_pid; /* 잠금을 얻은 프로세스의 pid */
<br>
- 수행 결과
![image](https://user-images.githubusercontent.com/86760744/182085667-18221d8a-7b6c-4688-a4f9-e7e1351bd26b.png)
<br><br>
## 동기화된 I/O (Synchronized I/O)
### I/O 동기화란?
- 기본적으로 리눅스 시스템은 메모리에 보존된 버퍼 캐시로부터 읽고, 쓰기 행위 시 버퍼 캐시로 쓴다.
- 버퍼가 가득 차거나 동기화 기능을 호출해 버퍼 캐시를 플러시 하기 전까지는 데이터를 디스크에 전송하지 않는다. (I/O Buffering)
- 이렇게 하면 필요한 경우 디스크에 기록하는 비교적 느린 기계적 프로세스를 줄임으로써 성능이 향상된다.
- 그럼에도 불구하고 응용 프로그램이 데이터가 디스크에 기록되는 시기를 제어하려는 경우가 있다.
- 이러한 용도로 리눅스 커널은 동기화된 작업을 위해 몇 가지 옵션을 제공한다.
<img width="653" alt="image" src="https://user-images.githubusercontent.com/86760744/182092506-4d8265f2-0693-4314-9a4b-60db4de5dadf.png">
<br><br>
## fsync()
``` c
#include <unistd.h>
int fsync (int fd);
데이터가 디스크에 도달했는지 확인하는 가장 간단한 방법은 fsync() 시스템 호출을 사용하는 것이다.
fsync()를 호출하면 파일 디스크립터 fd에 의해 매핑된 파일과 연관된 모든 더티 데이터가 디스크에 기록된다.
파일 디스크립터 fd는 쓰기 가능하도록 열려 있어야 한다.
하드 드라이브가 데이터와 메타 데이터가 디스크에 있다고 말할 때까지 반환되지 않는다.
성공 시, 0을 반환하고, 실패시 -1을 반환한다. (errno)
EBADF : 주어진 파일 디스크립터가 유효하지 않다.
EINVAL : 지정된 파일 디스크립터가 동기화를 지원하지 않는 오브젝트에 매핑된다.
EIO : 동기화 중에 하위 레벨 I/O 에러가 발생했다. (실제 I/O 오류)
fdatasync()
#include <unistd.h>
int fdatasync (int fd);
이 시스템 호출은 데이터만 플러시 한다는 점을 제외하고, fsync()와 동일한 작업을 수행한다.
이 호출은 메타 데이터가 디스크와 동기화 되었음을 보장하지 않으므로 잠재적으로 더 빠르다.
fsync / fdatasync 모두 파일을 포함하는 업데이트된 디렉토리 항목이 디스크와 동기화되도록 보장하지 않는다.
디렉토리 항목에 대한 업데이트도 디스크에 커밋되도록 하려면 디렉토리 자체에 대해 열린 파일 디스크립터도 fsync를 호출해야 한다.
sync()
#include <unistd.h>
void sync (void);
sync() 시스템 호출은 모든 버퍼를 디스크에 동기화하기 위해 제공된다.
이 함수에는 매개 변수가 없으며, 반환 값이 없다.
항상 성공하고, 모든 버퍼 (데이터 및 메타 데이터 모두)는 디스크에 기록된다.
open() 동기화 플래그 (O_SYNC)
O_SYNC 플래그는 open()에 전달되어 파일은 모든 I/O가 동기화 되어야 함을 나타낸다.
fd = open (file, O_WRONLY | O_SYNC);
write() 호출은 일반적으로 동기화되지 않는다.
O_SYNC 플래그는 write() 호출이 동기화된 I/O를 수행하도록 한다.
O_SYNC는 쓰기 작업에 대해 사용자 및 커널 시간을 더 소모한다.
또한 작성 중인 파일의 크기에 따라 I/O 대기 시간이 발생하기 때문에 경과 시간을 증가시킬 수 있다.
꼭 필요한 때가 아니라면 O_SYNC 플래그는 사용하지 않는다.
일반적으로 쓰기 작업이 디스크에 닿는 것을 보장해야하는 응용 프로그램은 fsync() 또는 fdatasync()를 사용한다.
보다 덜 호출될 수 있기 때문에 (특정 작업이 완료된 후에만 호출) O_SYNC보다 비용이 적게 드는 경향이 있다.
디렉토리 열기 (opendir)
디렉토리의 내용을 읽으려면 파일 디스크립터와 마찬가지로 디렉토리를 open하여 디렉토리 스트림을 생성해야 한다.
#include <sys/types.h>
#include <dirent.h>
DIR * opendir (const char *name);
opendir()은 성공 시 디렉토리 스트림 포인터를 정상적으로 반환하며, 실패 시 NULL 포인터를 반환한다.
디렉토리 스트림은 열린 디렉토리를 나타내는 파일 디스크립터, 일부 메타 데이터, 디렉토리 내용을 담을 수 있는 버퍼를 가리키는 포인터에 지나지 않는다.
디렉토리 스트림 뒤에 있는 파일 디스크립터는 dirfd()를 통해 얻을 수 있다.
int dirfd (DIR *dir);
dirdf()는 성공 시 파일 디스크립터를 정상적으로 반환하며, 실패 시 -1을 반환한다.
struct dirent {
ino_t d_ino; /* inode 번호 */
off_t d_off; /* 다음 dirent의 오프셋 */
unsigned short d_reclen; /* 해당 레코드의 길이 */
unsigned char d_type; /* 파일 타입 */
char d_name[256]; /* 파일 이름 */
};
응용 프로그램은 readdir()을 계속 호출하여 검색 중인 파일을 찾을 때까지,
또는 전체 디렉토리를 읽을 때까지(readdir()이 NULL을 반환할 때까지) 디렉토리의 각 파일을 가져온다.
실패하면 readdir()도 NULL을 반환한다.
오류와 모든 파일을 읽은 것을 구별하려면 응용 프로그램은 반환 값과 errno를 모두 확인해야한다.
EBADF - dir이 유효하지 않다.
디렉토리 닫기 (closedir)
#include <sys/types.h>
#include <dirent.h>
int closedir (DIR *dir);
파일과 마찬가지로 opendir()로 열린 디렉토리 스트림은 closedir()을 통해 닫아야한다.
07. 파일 I/O
파일 I/O 개요
파일 디스크립터 (File Descriptor)
파일 디스크립터 테이블 (File Descriptor Table)
파일 테이블 엔트리 (File Table Entry)
표준 파일 디스크립터 (Standard File Descriptor)
파일 생성 및 관리 (creat)
File mode bits
파일 생성 실습
int main() {
}
stat 예시
int main(int argc, char *argv[]) { struct stat sb; int ret;
}
권한(Permission) (chmod) 예시
int chmod(const char *pathname, mode_t mode);
int main() { char *filename = "./creat_example"; int mode = F_OK;
}
소유권(Ownership) (chown) 의 예시
int main() { char *filename = "./creat_example"; int mode = F_OK;
}
파일 열고 닫기 (close)
파일 열고 닫기 (fcntl)
CMD
파일 읽고 쓰기 (read)
파일 읽고 쓰기 (write)
파일 읽고 쓰기 (lseek)
lseek()는 열린 파일 디스크립터 fd의 위치 포인터를 offset 만큼 위치를 변경한다.
위치 변경 시 whence를 이용해 기준점을 정할 수 있다.
SEEK_SET
SEEK_CUR
SEEK_END
성공했을 경우 파일의 시작으로부터 멀어진 byte만큼의 offset을 반환한다.
실패했을 경우, -1을 반환한다. (errno)
실습 프로그램
define LOCK_T 0
define UNLOCK_T 1
define BUFSIZE 1024
int record_lock(int type, int fd, int start, int len);
int main(int argc, char **argv) { int fd; int record_start, record_len; char buf[BUFSIZE] = {0}; int i;
}
int record_lock(int type, int fd, int start, int len) { int ret; struct flock lock;
}
0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789
fdatasync()
sync()
open() 동기화 플래그 (O_SYNC)
디렉토리 열기 (opendir)
디렉토리 읽기 (readdir)
디렉토리 닫기 (closedir)
디렉토리 실습
int main(int argc, char argv[]) { struct dirent entry; DIR *dir; bool found = false;
}