시스템프로그래밍 4주차에는 리눅스의 파일에 관하여 배운다.
리눅스는 모두 파일로 저장이 되고 종류로는 Regular file, Special file, Directory, Symbolic link file... 등이 있다.
파일의 종류를 확인하고 싶으면 ls -l 명령어를 사용하면 된다.

정보 가장 앞에 나온 문자로 확인을 한다.
문자 | 파일의 종류 |
- | 일반 파일 |
d | 디렉토리 |
b | 블록 장치 특수 파일 |
c | 문자 장치 특수 파일 |
l | 심볼릭 링크 |
- Types of file
- Special file
장치와 데이터를 주고 받는 통로이다, 데이터 블록이 없으며 장치 번호를 inode에 저장한다.
단위에 따라 Character device file, Block device file로 나뉜다.
- Regular file
Text or binary data file이다.
- Directory(윈도우의 파일)
파일의 목록을 저장한 파일이다.
- Symbolic link file
이미 존재하는 파일이나 디렉토리에 접근 할 수 있는 새로운 이름이다.
- File organization
파일을 구성하는 요소는 3가지이다.
- File name
파일의 이름으로 사용자가 파일에 접근할 때 사용한다.
- inode
파일에 대한 정보를 저장한다, 번호를 통해 관리/접근 한다.
- Data block
실제 데이터가 저장된 디스크의 공간이다.

inode는 ls -i로 확인할 수 있다.
2주차에서 보았던 file table을 다시 보자면

file table은 직접 해당 파일을 가리키는 것이 아니라

i-node를 가리키고 inode에서 해당 파일을 가리키는 것이다.
시스템 콜을 사용을 해서 i-node에 저장되어 있는 파일 정보를 읽어 올 수 있다.
File information
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
file descriptor를 사용하면 fstat를 사용한다.
- pathname(file path or file descriptor)
파일의 경로(파일의 이름을 포함한)
-buf
파일의 정보를 저장한 주소
return: 0(success), -1(error)
당연히 파일에 읽기 권한이 있어야 한다.
struct stat의 구조체를 살펴보면
struct stat {
dev_t st_dev; /* [XSI] ID of device containing file */
ino_t st_ino; /* [XSI] File serial number */
mode_t st_mode; /* [XSI] Mode of file (see below) */
nlink_t st_nlink; /* [XSI] Number of hard links */
uid_t st_uid; /* [XSI] User ID of the file */
gid_t st_gid; /* [XSI] Group ID of the file */
dev_t st_rdev; /* [XSI] Device ID */
time_t st_atime; /* [XSI] Time of last access */
long st_atimensec; /* nsec of last access */
time_t st_mtime; /* [XSI] Last data modification time */
long st_mtimensec; /* last data modification nsec */
time_t st_ctime; /* [XSI] Time of last status change */
long st_ctimensec; /* nsec of last status change */
off_t st_size; /* [XSI] file size, in bytes */
blkcnt_t st_blocks; /* [XSI] blocks allocated for file */
blksize_t st_blksize; /* [XSI] optimal blocksize for I/O */
__uint32_t st_flags; /* user defined flags for file */
__uint32_t st_gen; /* file generation number */
__int32_t st_lspare; /* RESERVED: DO NOT USE! */
__int64_t st_qspare[2]; /* RESERVED: DO NOT USE! */
};
이렇게 작성이 되어 있다.
함수와 구조체를 이용하여 i-node의 정보를 가져오는 코드를 작성해보자.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void){
struct stat buf;
stat("stat.c", &buf);
printf("Inode = %d\n", (int)buf.st_ino);
printf("Mode = %o\n", (unsigned int)buf.st_mode);
printf("Nlink = %o\n", (unsigned int)buf.st_nlink);
printf("UID = %d\n", (int)buf.st_uid);
printf("GID = %d\n", (int)buf.st_gid);
printf("SIZE = %d\n", (int)buf.st_size);
printf("Atime = %ld\n", buf.st_atime);
printf("Mtime = %ld\n", buf.st_mtime);
printf("Ctime = %ld\n", buf.st_ctime);
printf("Blksize = %d\n", (int)buf.st_blksize);
printf("Blocks = %d\n", (int)buf.st_blocks);
return 0;
}
이렇게 우리가 작성한 stat.c의 정보를 가져오는 코드이다.
실행을 해보면

i-node 번호부터 시작해서 저장된 정보가 출력되는 것을 볼 수 있다.
st_mode에 저장된 데이터를 이용하면 파일의 타입까지 체크할 수 있다.
상수명 | 상수값(16진수) | 기능 |
S_IFMT | 0XF000 | st_mode 값에서 파일의 종류를 정의한 부분을 가져옴 |
S_IFIFO | 0X1000 | FIFO 파일 |
S_IFCHR | 0X2000 | 문자 장치 특수 파일 |
S_IFDIR | 0X4000 | 디렉토리 |
S_IFBLK | 0X6000 | 블록 장치 특수 파일 |
S_IFREG | 0X8000 | 일반 파일 |
S_IFLNK | 0XA000 | 심볼릭 링크 파일 |
S_IFSOCK | 0XC000 | 소켓 파일 |
임시로 파일을 만들고 해당 파일의 타입을 확인해보자.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void){
struct stat buf;
int kind;
stat("hello.txt", &buf);
printf("Mode = %d (Hexa: %x)\n", (unsigned int)buf.st_mode, (unsigned int)buf.st_mode);
kind = buf.st_mode & S_IFMT;
printf("Kind = %x\n", kind);
switch(kind){
case S_IFIFO:
printf("hello.txt : FIFO\n"); break;
case S_IFDIR:
printf("hello.txt: Directory\n"); break;
case S_IFREG:
printf("hello.txt: Regular File\n"); break;
}
return 0;
}
이렇게 and 연산에 의해 해당 파일의 모드가 출력되게 된다.

이렇게 and 연산 말고도 macro 함수를 사용할 수도 있기는 하다.
Checking permission
#include <unistd.h>
int access(const char *pathname, int mode);
-pathname
파일의 경로(파일 이름 포함)
-mode
R_OK, W_OK, X_OK, F_OK(파일의 존재를 확인)
#include <sys/errno.h>
#include <unistd.h>
#include <stdio.h>
extern int errno;
int main(void){
int per;
if(access("hi.txt", F_OK) == -1 && errno == ENOENT) printf("hi.txt: File not exist.\n");
per = access("hello.txt", R_OK);
if(per == 0) printf("linux.txt: Read permission is permitted.\n");
else printf("hello.txt: Read permission is not permitted.\n");
return 0;
}
return: 0(권한 있음), -1(error)

이렇게 파일의 권한이 있다고 보여지는 것을 볼 수 있다.
Changing permission
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
-pathname(file path or file descriptor)
파일의 경로
-mode
적용하려는 접근 권한
return: 0(success), -1(error)
해당 파일의 권한을 변경할 수 있다.
#include <sys/errno.h>
#include <unistd.h>
#include <stdio.h>
extern int errno;
int main(void){
int per;
if(access("hi.txt", F_OK) == -1 && errno == ENOENT) printf("hi.txt: File not exist.\n");
per = access("hello.txt", R_OK);
if(per == 0) printf("linux.txt: Read permission is permitted.\n");
else printf("hello.txt: Read permission is not permitted.\n");
return 0;
}

이렇게 변경되는 것을 볼 수 있다.
- Directory
이번에는 파일이 아닌 directory를 알아보자.
file name들과 file name이 가리키는 inode 번호를 담고 있는 파일을 directory라고 한다.
Current directory를 기준으로 작성한 파일 경로를 상대 경로라고하고, Root directory를 기준으로 작성한 파일 경로를 root directory라고 한다.
우선 우리가 사용했던 mkdir, rmdir, rename을 만들어보자.
#include <sys/stat.h>
#include <sys/types.h>
int mkdir (const char *pathname, mode_t mode);
-pathname
만들 directory의 이름이다.
-mode
만들 directory의 모드이다.
#include <unistd.h>
int rmdir(const char *pathname);
-pathname
삭제할 directory의 이름이다.
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
-oldpath
변경하고 싶은 directory의 이름이다.
-newpath
변경할 이름이다.
바로 실습을 해보면
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define PRINT_ERR_EXIT(_msg) {perror(_msg); exit(1);}
int main(void){
if(mkdir("system", 0755) == -1) PRINT_ERR_EXIT("system");
if(mkdir("programming", 0755) == -1) PRINT_ERR_EXIT("programming");
if(rename("system", "systemProgramming") == -1) PRINT_ERR_EXIT("systemProgramming");
if(rmdir("programming") == -1) PRINT_ERR_EXIT("programming");
return 0;
}
mkdir로 system directory와 programming directory를 만들고 system directory의 이름을 systemProgramming으로 변경한다.
그리고 programming directory를 제거하는 프로그램이다.

실습을 해보면 systemProgramming directory가 생성된 것을 볼 수 있다.
getcwd
#include <unistd.h>
char *getcwd(char *buf, size_t size);
현재 directory 주소를 가져오는 함수이다.
-buf
경로를 저장할 buffer의 주소
- size
buffer의 사이즈이다.
return: Path가 저장된 buf의 주소, NULL(error)
chdir
#include <unistd.h>
int chdir(const char *path);
working directory를 변경하는 함수이다.
-path
변경할 directory의 주소
return: 0(success), -1(fail)
#include <stdio.h>
#include <unistd.h>
int main(void){
char *cwd;
char wd[BUFSIZ];
cwd = getcwd(NULL, BUFSIZ);
printf("1. Current Directory : %s\n", cwd);
chdir("newDir");
getcwd(wd, BUFSIZ);
printf("2. Current Directory: %s\n", wd);
return 0;
}
이렇게 실습을 진행해보면
첫번째에 현재 디렉토리가 출력되는 것을 볼 수 있고, 두번째에 변경된 디렉토리가 출력되는 것을 볼 수 있다.

Open/Close a directory file
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
-name
열려는 디렉토리의 이름
return: 열린 디렉토리의 DIR 포인터, NULL(error)
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
-dirp
닫으려는 DIR 포인터
return: 0(success), -1(fail)
Reading directory information
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
-drip
읽으려는 directory file의 DIR 포인터
return: 현재 읽어온 항목의 dirent 구조체를 가리키는 포인터, NULL(더 이상 읽어올 항목이 없음)
여기서 dirent 구조체는
struct dirent {
__uint64_t d_ino; /* file number of entry */ \
__uint64_t d_seekoff; /* seek offset (optional, used by servers) */ \
__uint16_t d_reclen; /* length of this record */ \
__uint16_t d_namlen; /* length of string in d_name */ \
__uint8_t d_type; /* file type, see below */ \
char d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */ \
}
이렇게 구성이 되어 있다.
이 정보들을 하나씩 읽어오는 것이다.
위에서 만들었던 systemProgramming directory에 대충 파일을 하나 만든 후 실습을 해보자면
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
int main(void){
DIR *dp;
struct dirent *dent;
if((dp = opendir("systemProgramming")) == NULL){
perror("opendir: systemProgramming");
exit(1);
}
while((dent = readdir(dp))){
printf("Name : %s ", dent -> d_name);
printf("Inode : %d\n", (int)dent -> d_ino);
}
closedir(dp);
return 0;
}

이렇게 systemProgramming directory에 들어있는 모든 파일들이 순서대로 출력되는 것을 볼 수 있다.
- Link
이번에는 link에 대해 알아본다.
Hard link
먼저 하드 링크부터 알아본다.
해당 inode에 mapping 된 이름을 말한다.
동일한 file system 내에 여러 개의 hard link를 생성할 수 있고
링크 수 = hard link 수이며, 링크 수 = 0 은 파일 삭제를 의미한다.
Soft link
실제 파일의 경로명을 저장하는 파일이며, 동일한 inode를 사용하는 하드 링크와는 다르게 새로운 inode를 사용한다.
링크는 shell에서 ln을 사용해서도 만들 수 있다.
ln에 -s 명령어를 추가하면 soft link를 만든다.
우리는 이 명령어를 직접 만드는 실습을 해보자.
Making a link
#include <unistd.h>
int link(const char *oldpath, const char *newpath);
//Hard link 생성
#include <unistd.h>
int symlink(const char *target, const char *linkpath);
//soft link 생성
return: 0(success), -1(fail)
바로 하드 링크의 코드를 작성해보면
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(void){
struct stat buf;
stat("hello.txt", &buf);
printf("Before link count = %d\n", (int)buf.st_nlink);
link("hello.txt", "hello.ln");
stat("hello.txt", &buf);
printf("After Link Count = %d\n", (int)buf.st_nlink);
return 0;
}
새롭게 hello.ln의 파일이 생성되고 hello.txt의 링크 수가 증가하는 것을 볼 수 있다.

softlink는 매우 간단하다.
이렇게 간단하게 코드를 작성하고
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
symlink("hello.txt", "hello.sym");
return 0;
}
실행을 해보면

화살표로 링크를 나타내는 파일이 생긴 것을 볼 수 있다.
Removing a link
#include <unistd.h>
int unlink(const char *pathname);
#include <stdio.h>
int remove(const char *pathname);
링크를 제거하는 방법에는 이렇게 unlink와 remove 2가지 방법이 있다.
-pathname
삭제하려는 HardLink의 경로
return: 0(success), -1(error)
둘 다 File System에서 이름을 삭제하고 해당 파일이 마지막 Link인 경우, file을 삭제한다.
하지만 차이로는 remove는 directory에도 사용이 가능하지만, unlink는 불가능하다.
Get symbolic link file Info
#include <unistd.h>
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
Symbolic link에 저장된 데이터를 읽는 함수이다.
-pathname
읽을 파일의 경로
-buf
데이터를 저장할 buffer
-bufsiz
읽을 크기
return: 읽은 byte의 수, -1(error)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#define PRINT_CNT_INODE {\
printf("hello.txt : Link Count = %d\n", (int)buf.st_nlink);\
printf("hello.txt: Inode = %d\n", (int)buf.st_ino);}
int main(void){
struct stat buf;
printf("1. stat : hello.txt ---\n");
stat("hello.txt", &buf);
PRINT_CNT_INODE;
printf("2. stat : hello.sym ---\n");
stat("hello.sym", &buf);
PRINT_CNT_INODE;
printf("3. lstat : hello.sym ---\n");
lstat("hello.sym", &buf);
PRINT_CNT_INODE;
}
이렇게 hello.sym가 가진 정보를 출력해보면 lstat로 가져온 정보만 다르다는 것을 볼 수 있다.

#include <limits.h>
#include <stdlib.h>
char *realpath(const char *path, char *resolved_path);
Symbolic link가 가리키는 파일의 절대 경로를 얻는다.
-path
symbolic link file의 경로
-resolved_path
결과 값을 저장할 buffer
return: 결과가 저장된 buffer의 포인터, NULL(error)
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define PRINT_ERR_EXIT(_msg) {perror(_msg); exit(1);}
int main(void){
char buf[BUFSIZ];
int n;
n = readlink("hello.sym", buf, BUFSIZ);
if(n == -1) PRINT_ERR_EXIT("readlink");
buf[n] = '\0';
printf("hello.sym : READLINK = %s\n", buf);
realpath("hello.sym", buf);
printf("hello.sym : READPATH = %s\n", buf);
return 0;
}

이렇게 link로 가져온 파일의 원래 위치를 가져온다.
'학교 생활 > 시스템 프로그래밍' 카테고리의 다른 글
시스템 프로그래밍 6주차 (0) | 2023.03.07 |
---|---|
시스템 프로그래밍 5주차 (0) | 2023.03.05 |
시스템 프로그래밍 3주차 (0) | 2023.01.16 |
시스템 프로그래밍 2주차 (2) | 2023.01.15 |
시스템 프로그래밍 1주차 (0) | 2023.01.14 |