728x90

시스템프로그래밍 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로 가져온 파일의 원래 위치를 가져온다.

+ Recent posts