5. 파일 시스템

김동웅·2021년 9월 12일
0

UNIX

목록 보기
6/14

핵심개념

• 표준 유닉스 파일 시스템은 부트 블록, 슈퍼 블록, i-리스트, 데이터블록 부분으로 구성된다

• 파일 입출력 구현을 위해서 커널 내에 파일 디스크립터 배열, 파일테이블, 동적 i-노드 테이블 등의 자료구조를 사용한다.

• 파일 하나당 하나의 i-노드가 있으며 i-노드 내에 파일에 대한 모든 상태 정보가 저장되어 있다.

• 디렉터리는 일련의 디렉터리 엔트리들을 포함하고 각 디렉터리 엔트리는 파일 이름과 그 파일의 i-노드 번호로 구성된다.

• 링크는 기존 파일에 대한 또 다른 이름으로 하드 링크와 심볼릭(소프트) 링크가 있다.

5.1 파일 시스템 구현

파일 시스템 구조

  • 부트 블록(Boot Block)
    : 파일 시스템 시작부에 위치하고 보통 첫번째 섹터를 차지함.
    : 리눅스 시작시 사용되는 부트스트랩 코드가 저장되는 블록.

  • 슈퍼 블록(Super Block)
    : 전체 파일 시스템에 대한 정보를 저장.
    : 총 블록수, 사용가능한 i-노드 개수, 사용 가능한 블록 비트맵,
    블록의 크기, 사용중인 블록 수, 사용 가능한 블록 수 등.

  • i-노드 리스트 (i-Node List)
    : 각 파일을 나타내는 모든 i-노드들의 리스트
    : 한 블록은 약 40 개 정도의 i-노드를 포함

  • 데이터 블록(Data block)
    : 파일의 내용(데이터)를 저장하기 위한 블록들


i-노드(i-Node)

  • 한 파일은 하나의 i-노드를 갖는다.

  • 파일에 대한 상태 정보를 가지고 있음.
    ex) 파일 타입 : 일반 파일, 디렉터리, 블록장치, 문자 장치 등
    파일크기
    사용권한
    파일 소유자 및 그룹
    접근 및 갱신 기간
    데이터 블록에 대한 포인터(주소) 등


블록 포인터

  • 데이터 블록(파일의 내용(데이터)을 저장하는 블록) 에 대한 포인터

  • 하나의 i-노드 내의 블록 포인터

  1. 직접 블록 포인터 10개.
  2. 간접 블록 포인터 1개.
  3. 이중 간접 블록 포인터 1개.
  • 최대 몇개의 데이터블록을 가리킬 수 있을까?
  1. 직접 블록포인터 10개 -> 각 1개씩 10개의 데이터블록 가리킬 수 있음.

  2. 간접 블록포인터 1개 -> 1024개의 직접 블록포인터를 가리킬 수 있음
    -> 1024개의 데이터블록을 가리킬 수 있음.
    - 블록 포인터 크기 4바이트, 한 블록의 크기 4096바이트 일때 간접 블록 포인터가 가리키는 데이터 블록 내의 1024개의
    직접 블록 포인터를 저장할 수 있음.

  3. 이중 간접 블록 포인터 -> 1024개의 간접 블록 포인터를 가리킬 수 있음 -> 1024*1024개의 데이터블록을 가리킬 수 있음.

<총 10 + 1024 + (1024*1024) 개의 데이터 블록을 가리킬 수 있음>


파일 입/출력 구현

       ▲ 1	       ▲ 2               ▲  3                ▲  4
  • 파일 입출력 구현을 위한 커널 내 자료구조
    1. 파일 디스크립터 배열(Fd array)
    2. 열린 파일 테이블(Open file table)
    3. 동적 i-노드 테이블(Active i-node table)
자세한 설명은 아래에서..

1. 파일 디스크립터 배열(Fd array)

  • 프로세스당 하나씩 갖는다.

  • 파일 디스크립트 배열
    : 프로세스 내의 자료구조
    : 프로세스 내에서 열린 파일의 파일 디스크립터를 저장하기 위한 구조
    : 열린 파일 테이블 엔트리를 가리킴.

파일 디스크립터란?

  • 파일 디스크립터의 배열의 인덱스
  • 열린 파일을 나타내는 번호

2. 열린 파일 테이블(Open file table)

  • 파일 테이블(file table)
    : 커널 내의 자료구조
    : 열려진 모든 파일 목록
    : 파일 테이블 엔트리로 구성
    : 파일을 열 때 마다 파일 테이블 엔트리가 만들어짐

  • 파일 테이블 엔트리(FIle table entry)
    : 파일 상태 플래그 (read, write, append)
    : 파일의 현재 위치 (current file position)
    : 동적 i-node에 대한 포인터

3. 동적 i-노드 테이블

  • 동적 i-노드 테이블
    : 커널 내의 자료구조
    : 열린 파일들의 i노드를 저장하는 테이블
    : 열린 파일의 i노드의 모든 정보를 가져옴

  • i-노드
    : 하드디스크에 저장되어 있는 파일에 대한 자료구조
    : 한 파일에 하나의 i-노드
    : 하나의 파일에 대한 정보 저장
    ex) 소유자, 크기, 파일이 위치한 장치, 파일 내용 디스크블록에 대한 포인터


파일을 위한 커널 구조

1. i-리스트에서 i-노드를 찾아서 동적 i-노드 테이블로 가져와 테이블 내에 하나의 엔트리를 만듦

2. 열린 파일 테이블에도 하나의 엔트리를 만들어 파일 위치, 플래그, 동적 i-노드에 대한 포인터 저장

3. 파일 디스크럽터 배열에 엔트리를 만들어 인덱스(파일 디스크럽터) 반환


한 파일을 두 번 열 때 자료구조

  • 이미 해당 파일의 i-노드 내용이 동적 i-노드 테이블에 존재하기 때문에
    열린 파일 테이블 내에 새로운 엔트리를 만들어야함.
    - 현재 파일 위치, 파일 상태 플래그를 새로 설정해야 하기 때문
  • fd 배열에도 새로운 엔트리 만들어 fd반환

fd = dup(3); 혹은 fd = dup2(3,4) 수행과정

  • fd배열 내에만 새로운 엔트리를 만듦.

  • 열린 파일 테이블 내의 동일한 파일 엔트리를 가리키도록 함.


열린 파일에 대해 읽거나 쓸때(read or write), 데이터 블록의 위치를 찾는 방법

  • 파일 테이블 엔트리 내에 저장된 현재 파일위치 정보 + 동적 i-노드 내의 블록 포인터 정보 이용


5.2 파일 상태 정보

  • 파일 상태(file status)
    : 파일에 대한 모든 정보
    : 블록 수, 파일 타입, 사용 권한, 링크 수, 사용자 ID, 그룹 ID, 파일 크기, 수정시간 등
    ex) $ls -l hello.c
    2 -rw-r--r-- 1 chang cs 617 11월17일15:53 hello

  • 상태 정보 : stat()
    : 파일 하나당 하나의 i-노드가 있으며
    : i-노드 내에 파일에 대한 모든 상태 정보가 저장되어 있다.
    : lstat()과 stat()의 차이는 lstat은 대상이 심볼릭 링크일 때 링크가 가리키는 파일이 아니라 링크 자체에 대한 정보

int stat(const char *filename, struct stat *buf);
int fstat(int fd, struct stat * buf);
int lstat(const char *filename, struct stat *buf);

// 파일의 상태정보를 가져와서 stat 구조체 buf에 저장한다.
성공하면 0, 실패하면 -1리턴
  • stat 구조체
struct stat {
mode_t st_mode; // __파일 타입과 사용권한__
ino_t st_ino; // i-노드 번호
dev_t st_dev; // 장치 번호
dev_t st_rdev; // 특수 파일 장치 번호
nlink_t st_nlink; // 링크 수
uid_t st_uid; // 소유자의 사용자 ID 
gid_t st_gid; // 소유자의 그룹 ID
off_t st_size; // 파일 크기
time_t st_atime; // 최종 접근 시간
time_t st_mtime; // 최종 수정 시간
time_t st_ctime; // 최종 상태 변경 시간
long st_blksize; // 최적 블록 크기
long st_blocks; // 파일의 블록 수
};

`

  • 파일타입

  • 파일 타입 검사 함수

맞으면 1 아니면 0 리턴

ex) 파일타입 검사함수 ftype.c

#include <sys/types.h> 
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* 파일 타입을 검사한다. */
int main(int argc, char *argv[])
{ 
  int i;
  
  
  struct stat buf;
  
  	인자로 받은 개수만큼 검사
    for (i = 1; i < argc; i++)
    {
   	 printf("%s: ", argv[i]);
     	
        // 파일정보를 가져오는데 실패하면 error메세지 출력 
     	if (lstat(argv[i], &buf) < 0)
        {
            perror("lstat()"); 
    	    continue;
    	}
       
      if (S_ISREG(buf.st_mode)) // st_mode 필드에 파일 타입 저장됨
      printf("%s \n", "일반 파일");
      if (S_ISDIR(buf.st_mode)) 
      printf("%s \n", "디렉터리");
      if (S_ISCHR(buf.st_mode)) 
      printf("%s \n", "문자 장치 파일");
      if (S_ISBLK(buf.st_mode)) 
      printf("%s \n", "블록 장치 파일");
      if (S_ISFIFO(buf.st_mode)) 
      printf("%s \n", "FIFO 파일");
      if (S_ISLNK(buf.st_mode)) 
      printf("%s \n", "심볼릭 링크");
      if (S_ISSOCK(buf.st_mode)) 
      printf("%s \n", "소켓");
      
   }
    exit(0);
 }
 
  • 파일 사용권한

: 각파일에 대한 권한관리 (owner/ group / others) 로 구분해서 관리한다.
권한 종류 : 읽기 r , 쓰기 w, 실행 x

  1. read 권한 있어야 O_RDONLY, O_RDWR 을 사용하여 파일을 열 수 있다.
  2. write 권한 있어야 O_WRONLY, O_RDWR, O_TRUNC 사용하여 파일을 열 수 있다.
  3. 디렉토리에 write 권한과 execute권한이 있어야 디렉토리 내의 파일 생성 및 삭제 가능
  • 파일 사용권한 변경함수 : chmod()
  int chmod (const char *path, mode_t mode );
  int fchmod (int fd, mode_t mode );

  // 리턴 값 -1 실패, 0 성공
  // mode : 8진수 형태의 세자리 정수 ex) 664 

ex) fchmod.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

/* 파일 사용권한을 변경한다. */
int main(int argc, char *argv[])
{
long strtol( );

int newmode;

newmode = (int) strtol(argv[1], (char **) NULL, 8); // string to long (8진수)

if (chmod(argv[2], newmode) == -1) {
	perror(argv[2]);
	exit(1);
}
exit(0);
}
  • 파일의 최종 접근시간과 최종 변경시간 조정 : utime()

struct utimbuf {
time_t actime; /* access time */
time_t modtime; /* modification time */
}


5.3 디렉터리(folder)

  • 디렉터리의 구조 :

루트 디렉터리부터 시작하여 트리 구조 형성

디렉터리 안의 디렉터리를 해당 디렉터리의 서브디렉터리라고 한다.

디렉터리 구현

디렉터리 엔트리 : 각 엔트리는 디렉터리 내 하나의 파일을 나타냄
파일 이름과 그 파일의 i-노드 번호로 구성

opendir(), readdir() 함수

  • opendir()
    : 디렉터리 열기 함수
    : dir 포인터 (열린 디렉터리를 가리키는 포인터) 리턴

  • readdir()
    : 디렉터리 읽기 함수

  • dir 구조체 : 디렉터리에 대한 정보를 저장하기 위한 구조체


#include <dirent.h> 
struct dirent
{
ino_t d_ino; // i-노드 번호
char d_name[NAME_MAX + 1]; // 파일 이름
}

 #include <sys/types.h>
 #include <dirent.h>
 
 DIR *opendir(const char *path);
 // path 디렉터리를 열고 성공하면 dir구조체 포인터를 리턴, 실패하면 NULL리턴
 
 struct dirent *readdir(DIR *dp);
 // 한번에 디렉터리 엔트리를 하나씩 읽어서리턴한다.

ex1 ) list1.c

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char* argv[])
{
	
	DIR* dp;   // dir 구조체 포인터	 
 	char *dir;  // path 디렉터리 포인터  
	
	struct stat st;  // 파일의 상태 정보를 담는 구조체 st
	struct dirent *d; // dir구조체 포인터 d
	char path[BUFSIZ+1];

	if(argc==1)  // 입력받은 인자가 없는경우 
	{
		dir= ".";
	}
	
	else
	{
		dir = argv[1]; // 입력받은 인자가 있는경우 
	}

	// dir !구조체! 포인터 변수 dp에 opendir(dir)수행후 dir구조체 포인터 리턴 
    	// dp는 입력받은 디렉토리에 대한 정보를 저장한 구조체를 가리키는 포인터이다.
       
	if(( dp = opendir(dir))== NULL) // 디렉터리를 열기 
		perror(dir);
	
	
	// 
	while( (d=readdir(dp))!=NULL) // 각 디렉터리 엔트리에 대해 
	{
		printf("%s\n", d->d_name); // 파일 이름 프린트 	
	}
	
	closedir(dp);
	exit(0);
}


파일 이름/크기 출력

st
while((d = readdir(dp)) != NULL) // 디렉터리 내의 각 파일
{
// path = dir/d->name 저장 
sprintf(path, "%s/%s", dir, d->d_name); // 파일 경로명 만들기

if(lstat(path, &st) < 0)	// 파일 상태 가져오기
	perror(path);
    
printf("%5d %s", st->st_blocks, d->name); // 블록 수, 파일 이름 출력
}

st_mode

  • lstat() 시스템 호출
    : 파일 타입과 사용권한 정보는 st->st_mode 필드에 함께 저장됨

  • st_mode 필드

디렉터리 만들기 : mkdir() 시스템 홏풀

  • path 가 나타내는 새로운 디렉터리를 만든다.
  • ".",와 ".." 파일은 자동적으로 만들어짐
#include <sys/types.h>
#include <sys/stat.h>
int mkdir (const char *path, mode_t mode );
새로운 디렉터리 만들기에 성공하면 0, 실패하면 -1을 리턴한다.

디렉터리 삭제 : rmdir() 시스템 호출

  • path가 나타내는 디렉터리가 비어 있으면삭제한다.
#include <unistd.h>
int rmdir(const char *path);
// 디렉터리가 비어있으면 삭제. 성공하면 0 ,실패시 -1리턴

디렉터리 구현

: 디렉터리를 위한 구조는 따로 없음 ( 일종의 파일로 다른 파일처럼 구현됨)
: __디렉터리도 다른 파일처럼 하나의 i-노드로 표현됨.
: 디렉터리의 내용은 디렉터리 엔트리(파일이름, i-노드번호로 구성)


링크

  • 링크는 기존 파일에 대한 또 다른 이름이다.
  • 하드 링크(link()이용)와 심볼릭(소프트) 링크 ( symlink() 이용) 이 있다.

#include <unistd.h>

int link(char *existing, char *new);

// 기존 파일 이름 existing 에 대한 새로운 링크 new를 만든다.
// 성공하면 0 실패하면 -1 리턴

int unlink(char* path);
// 링크 path를 제거함
// 성공하면 0, 실패하면 -1 리턴
  • link.c
#include <unistd.h>
int main(int argc, char *argv[ ])
{
  if (link(argv[1], argv[2]) == -1) {
  exit(1);
  }
exit(0);
}
$ link a.txt b.txt
// a.txt에 대한 링크 b.txt를 만듦

$ ls -l a.txt b.txt
// a.txt와 b.txt에 대한 정보 보기
-> 
-rw-rw-r-- 2 kdo6301 kdo6301 4 Sep 15 16:17 a.txt
-rw-rw-r-- 2 kdo6301 kdo6301 4 Sep 15 16:17 b.txt

$ ls -i a.txt b.txt
// a.txt와 b.txt에 대한 i-노드 인덱스 보기
->
675097 b.txt  675097 test.txt

  • unlink.c
#include <unistd.h>
main(int argc, char *argv[ ])
{
	int unlink( );
	if (unlink(argv[1]) == -1) {
	perror(argv[1]);
	exit(1);
	}
  exit(0);
}
$ unlink b.txt
// b.txt를 언링크
링크 수가 0 이되면 파일 삭제됨

하드링크 vs 심볼릭링크

  • 하드 링크 (Hard link)
    : 지금까지 살펴본 링크
    : 파일 시스템 내의 i-노드를 가리킴
    : 같은 파일 시스템 내에서만 사용가능

  • 심볼릭 링크 (symbolic link)
    : 소프트링크 라고도 한다.
    : 실제 파일의 경로명 저장하고 있는 링크
    : 파일에 대한 간접적인 포인터역할
    : 다른 파일 시스템에서도 사용가능

  • 심볼릭 링크

int symlink(const char* actualpath, const char* sympath);
// 심볼릭 링크를 만드는데 성공 -> 0리턴, 실패 -> -1리턴
  • symbolic.c

#include <unistd.h>
int main(int argc, char* argv[])
{
	if(symlink(argv[1],argv[2])==-1)
    {
    exit(1);
    }
    exit(0);
    
}
$ slink a.txt c.txt
-> a.txt에 대한 심볼릭 링크 c.txt를 만듦
$ ls -l c.txt
c.txt에 대한 리스트 출력
  • 심볼릭 링크 내용
#include <unistd.h>
int readlink(const char* path, char *buf, size_t bufsize);
// path 심볼릭 링크의 실제 내용(경로명을 담고 있음)을 읽어서 buf에 저장한다.
// 성공하면 buf에 저장한 바이트 수를 반환하며 실패하면 -1을 반환한다.
  • rlink.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
     char buffer[1024];
    int nread;
    
    nread = readlink(argv[1], buffer, 1024);
    
    if(nread > 0)
    {
    	write(1,buffer,nread);
        exit(0);
    }
    
    else
    {
   	fprintf(stderr,"오류 : 해당 링크 없음\n");
        exit(1);
    }
}
    
    




 

0개의 댓글

관련 채용 정보