Pintos Project 2-2) UserProg- File manipulation

Jisu·2023년 5월 8일
0

PintOS

목록 보기
3/6
post-thumbnail

PintOS Project 2_ User Program: File manipulation 구현

PintOS 파일시스템 및 manipulation에 대한 글입니다.

 

  • File manipulation

    • 파일 디스크립터 개념
    • 리눅스 파일시스템 구조
    • File sharing
    • Open File Table의 존재 이유
  • Open

    • 시스템 콜 구현 및 Flow Chart
    • 구조체
  • Remove

  • Denying Writes to Executables

 


File Manipulation

File descriptor

  • 파일 디스크립터(fd) 개념
    • 리눅스/유닉스 계열 시스템에서 프로세스가 file을 다룰 때 사용하는 개념
    • 유닉스 시스템에서는 모든 것을 파일로 정의
      • 프로세스가 파일을 Open하면 커널은 해당 프로세스의 fd 중 사용하지 않는 가장 작은 값 할당
      • 그 다음 프로세스가 열려있는 파일에 시스템 콜을 통해 접근할 때, fd 값을 이용해 파일 지칭
      • 프로그램이 프로세스 메모리에서 실행될 때, 기본 할당 fd는 stdin(0), stdout(1), stderror(2)
  • 파일 디스크립터 존재 이유
    - 리눅스 파일 시스템은 다음과 같이 Open File Table을 거쳐 실제 하드디스크로 연결되어 있음.
    - 파일에 읽고 쓰려면 실제 알아야 하는게 inode table
    - 이는 프로세스가 직접 접근할 수 없으므로  유저 프로그램에서 open()  시스템 콜을 호출했을 때 리턴되는 fd만 커널에 넘겨주고, 실제 파일 링크를 가져오는 일은 커널에서 함.
    - 파일 디스크립터는 어떠한 유닉스 파일 타입도 참조할 수 있음. 디렉토리, 소켓, named pipes 

File descriptor table vs Open file table

파일시스템 구조

  • 리눅스 시스템은 프로세스마다 file descriptor table을 갖고 있고
  • 커널은 모든 열려진 파일을 관리하는 파일 테이블(Open File Table)을 가짐
    • File Table 항목 엔트리는 파일 상태 flag, offset, vnode 테이블 위치 정보를 포함.
  • vnode table은 inode 정보와 파일 크기(data size) 등 포함

File sharing

  • 하나의 파일에서 같은 파일을 두 번 열었을 때
    • 같은 파일을 두 번 열면 서로 다른 파일 디스크립터를 부여하고 서로 다른 파일 오프셋을 유지
    • 즉, 프로세스에서 파일 입출력은 open 함수로 연 작업을 구분하는 것이며 실제 물리적인 파일이 같은지는 구분하지 않음
    • 프로세스의 fd_table에는 각각의 fd를 부여, 커널에도 각각의 파일 상태와 작업위치(pos)를 가지나, 결국 같은 vnode를 참조
  • 부모 프로세스의 Open file을 자식 프로세스로 복제했을 때
     

Open File Table의 존재 이유

1. 가상화 및 abstraction

  • 모든 프로세스는 하나의 머신과도 같으며, 자신이 독립적으로 자원을 쓴다고 착각해야 함.
  • 각각의 프로세스는 독립적인 file discriptor table이 있지만, 컴퓨터 관리 측면에선 ‘System-wide’한 오픈 테이블이 있어야 함
    • 이는 로컬변수와 전역변수의 존재 의미와도 같으며, VM에서 user virtual address는 per-process, kernel virtual address는 global(system wide)인 것도 같은 맥락
  • 커널 차원의 Open File Table 역할 
    - 파일마다 여러 프로세스에 의해 참조된(open) 횟수를 count하여, 0이되면 알아서 해제
    - vnode table 메타데이터를 찾아가기 위한 중간다리

2. 유연성

  • 파일 작업 간소화
    • 프로세스 및 Linux 커널 내부의 파일 객체로부터 파일 경로를 분리
  • 동기화 용이
    • fd를 사용하면 프로세스에서 동일한 파일을 다른 목적 및 위치로 여러 번 열 수 있음 
    • 프로세스가 동일한 파일에 대해 여러 개의 독립적인 참조를 가질 수 있으므로 동시에 액세스 가능
    • 프로세스가 읽기 및 쓰기 작업을 동시에 수행하거나 동일한 파일 내에서 여러 위치를 유지해야 하는 경우 유용
    • 예를 들어, 한 fd가 쓰기 작업을 수행하여 파일 위치를 앞당기면 동일한 파일 객체를 참조하는 다른 fd들도 업데이트된 위치를 볼 수 있음
  • 메모리 관리
    • 여러 파일 기술자가 동일한 파일 객체를 참조할 경우,
      • 서로 다른 fd가 기본 파일 관련 데이터 구조를 복제하지 않고도 동일한 파일 객체를 공유 가능
  • 다만 핀토스의 경우 ‘write’에 있어 async(비동기) 처리를 추구하여
    • 파일을 열 때마다 file_deny_write() 로 실행 중인 파일에 파일에 write 하지 못하도록 구현해야 함.

 


Open() 시스템 콜 구현 및 Flow Chart

  • 전반적인 흐름
    • 프로세스는 파일 fd를 커널로 보내고(system call open() 
    • 커널은 프로세스를 대신해 파일에 액세스, 파일구조체 리턴
  • open()의 핵심은 inode를 통 파일객체를 가져오는 과정
    • 유저→커널을 거쳐 실제 ‘actual file’ 구조체를 가져오는 과정은 첫 문장 하나에 집약되어 있음
    • filesys_open의 dirlookup은 해당 파일의 inode를 찾아 리턴
    • 그 inode로 실제 파일을 열어, *fileobj에 할당됨
  • inode를 찾아갈 수 있는 이유는, 디렉토리 첫주소랑 오프셋만 알면 Open File Talbe에서 *inode 가리키는 포인터를 찾을 수 있기 때문

open()

int open (const char file) {
        struct file *fileobj = filesys_open(file); // 열 파일 객체 정보를 받기
        ...
	int fd = add_file_to_fd_table(fileobj);  
        ...
	return fd;
}

file_open()

  • inode라는 포인터를 새 구조체 struct file로 옮기는 작업
    • file object 속성을 초기화
struct file *
file_open (struct inode *inode) {
	struct file *file = calloc (1, sizeof *file);
	if (inode != NULL && file != NULL) {
		file->inode = inode;
		file->pos = 0;
		file->deny_write = false;
		return file;
	} else {
		inode_close (inode);
		free (file);
		return NULL;
	}
}

dir_lookup()

dir_lookup (const struct dir *dir, const char *name,
		struct inode **inode) {
	...
   if (lookup (dir, name, &e, NULL))	// 성공시 inode에 할당
   *inode = inode_open (e.inode_sector);
	...
}
  • lookup() : 디렉토리 첫주소에서 오프셋만큼 이동해 파일 위치를 찾음
    • *sets EP to the directory entry
    • *sets OFSP to the byte offset of the directory entry

add_file_to_fd_table()

  • 파일을 새로 열 때마다 fd_idx++  (인덱스 늘리기)
  • fd_table의 해당 인덱스에 file 구조체 넣기. fdt[cur->fd_idx] = file 
int add_file_to_fd_table(struct file *file) {
	struct thread *cur = thread_current();
	struct file **fdt = cur->fd_table;
	while (cur->fd_idx < FDCOUNT_LIMIT && fdt[cur->fd_idx]) {
		cur->fd_idx++;		       
	}
	if (cur->fd_idx >= FDCOUNT_LIMIT)
		return -1;			// 빈공간이 없으면 에러처리 
	
	fdt[cur->fd_idx] = file;
	return cur->fd_idx;
}

 

핀토스 파일 디스크립터 구조

  • fd를 통해 파일에 접근
  • fd는 fd_table의 인덱스,
  • fd_table는 파일 구조체를 가리키는 포인터 배열
    - 프로세스에서 시스템 콜로 파일을 열 때마다 fd_idx가 하나씩 +1

파일 구조체

/* An open file. */
struct file {
	struct inode *inode;        /* File's inode. */
	off_t pos;                  /* Current position. */
	bool deny_write;            /* Has file_deny_write() been called? */
};

 
inode 구조체

  • 각 inode는 file object 속성과 disk block 위치 저장_
  • object 속성에는 메타데이터(마지막 변경시간), 소유자, 실행권한(read/write), 파일유형 
/* In-memory inode. */
struct inode {
	struct list_elem elem;              /* Element in inode list. */
	disk_sector_t sector;               /* Sector number of disk location. */
	int open_cnt;                       /* Number of openers. */
	bool removed;                       /* True if deleted, false otherwise. */
	int deny_write_cnt;                 /* 0: writes ok, >0: deny writes. */
	struct inode_disk data;             /* Inode content. */
};
/* On-disk inode.
 * Must be exactly DISK_SECTOR_SIZE bytes long. */
struct inode_disk {
	disk_sector_t start;                /* First data sector. */
	off_t length;                       /* File size in bytes. */
	unsigned magic;                     /* Magic number. */
	uint32_t unused[125];               /* Not used. */
};
  • return inode->data.length : 파일 사이즈 리턴

 
디렉토리 구조체

/* A directory. */
struct dir {
	struct inode *inode;                /* Backing store. */
	off_t pos;                          /* Current position. */
};
/* A single directory entry. */
struct dir_entry {
	disk_sector_t inode_sector;         /* Sector number of header. */
	char name[NAME_MAX + 1];            /* Null terminated file name. */
	bool in_use;                        /* In use or free? */
};
  • When a file is opened by a process, the operating system reads the inode from disk into memory and creates an in-memory inode data structure to represent it.
  • disk_sector_t contains information about the sector number on disk where the inode is stored,
  • struct inode_disk stores the actual contents of the inode on disk.

 

생각 정리

  • OS도 결국은 자료구조다. hard link로 연결된-
    • fd는 ‘링크’로서 실제 하드디스크 파일을 가리키고 있고. 전부 주소로서 접근한다. 
    • 즉, 파일명(우리가 입력하는)과 그 파일 실체를 연결하는 건 하드링크라는 것. 이는 directory entry라고도 한다. 
    • 하드링크는 프로세스에서 파일을 열 때마다 생기지만, 같은 파일을 가리킬 수 있고. 
    • 경우에 따라 동시접근에 따른 inconsistency를 방지하기 위해 
      • deny_write
      • read/write시 lock,  semaphore를 이용한다.

 

  • remove() 는 실제 하드디스크 파일을 삭제하는 게 아니라 ‘링크’를 삭제하는 것이다.
    • 파일 이름과 파일 데이터 사이의 링크를 제거하며, 파일 데이터는 디스크에 남아있음. 
    • Open File Table은 최근 액세스된 파일들을 추적하는, 모든 프로세스에 의해 접근되는 테이블이며 reference count가 0이 되면 테이블에서 지운다. 
    • 결국 이러한 directory-based 파일시스템에선 ‘파일명’과 ‘path’를 분리하여 유저프로그램이 fd(식별자)만으로 여러 파일에 읽고쓰게 해주며,
    • 다이렉트 접근이 아닌 제한적 접근으로 프로세스로부터 메모리 영역을 보호하기도 한다. 

Denying Write to Executables 

  • 구현 목표
    • 실행 중 파일에 데이터를 기록하는 것을 방지
      • 실행 중 파일 데이터가 변경되면 프로그램이 원래 예상했던 데이터와 다르게 변경된 데이터를 읽어 올 가능성
      • 이로 인해 올바른 연산 결과를 도출할 수 없기 때문에 OS 차원에서 실행 중 유저 프로그램에 대한 쓰기 접근을 막아야 함.
  • 과제 해결
    • 파일이 실행되는 위치를 정확히 파악
    • 실행 중인 파일의 디스크 상 파일이 변경되는 것을 방지
      • file_deny_write : 메모리에 프로그램 적재 시, 프로그램 파일에 쓰기 권한 제거
      • file_allow_write : 프로그램이 종료되었을 때, 프로그램 파일에 쓰기 권한 부여

0개의 댓글

관련 채용 정보