운영체제(구현) - 핀토스-2(파일 디스크립터)

연도·2024년 6월 5일
0

운영체제 이론&구현

목록 보기
11/19
post-thumbnail

구현 전 알아야 할 것

구현해야 할 것

System Call함수들

open

파일 열기

구현전 힌트

  • 파일을 open
  • 해당 파일 객체에 파일 디스크립터 부여
  • 파일 디스크립터 리턴
  • 해당 파일이 존재하지 않으면 -1 리턴
int 
open(const char *file)
{
	check_address(file);
	struct file *f = filesys_open(file); 

	if (f == NULL)
	{
		return -1;
	}

	int fd = process_add_file(f);

	if (fd == -1)
	{
		file_close(f); 
	}
	return fd;
}

file_size

파일의 크기 탐색

구현 전 힌트

  • 파일 디스크립터를 이용하여 파일 객체 검색
  • 해당 파일의 길이를 리턴
  • 해당 파일이 존재하지 않으면 -1 리턴
int filesize(int fd)
{
	// 1. 현재 스레드 정보 가져오기
	struct thread *curr = thread_current();
	
    // 2. 파일 디스크립터 유효성 검사
	if (fd < 2 || fd >= FD_MAX || curr->fd_table[fd] == NULL)
	{
		return -1; 
	}

	struct file *f = curr->fd_table[fd];
	return file_length(f);
}

read

열린 파일의 데이터를 읽기

구현 전 힌트

  • 파일에 동시 접근이 일어날 수 있으므로 Lock 사용
  • 파일 디스크립터를 이용하여 파일 객체 검색
  • 파일 디스크립터가 0일 경우 키보드에 입력을 버퍼에 저장 후
    버퍼의 저장한 크기를 리턴 (input_getc() 이용)
  • 파일 디스크립터가 0이 아닐 경우 파일의 데이터를 크기만큼 저
    장 후 읽은 바이트 수를 리턴
int 
read(int fd, void *buffer, unsigned size)
{
	check_address(buffer);
	struct thread *curr = thread_current();
	lock_acquire(&filesys_lock); 

	if (fd < 0 || fd >= FD_MAX || buffer == NULL)
	{
		lock_release(&filesys_lock);
		return -1;
	}

	if (fd == 0)
	{ 
		for (int i = 0; i < size; i++)
		{
			((char *)buffer)[i] = input_getc();
		}

		lock_release(&filesys_lock);
		return size;
	}

	struct file *f = curr->fd_table[fd];

	if (f == NULL)
	{
		lock_release(&filesys_lock);
		return -1;
	}

	int bytes_read = file_read(f, buffer, size); 
	lock_release(&filesys_lock);
	return bytes_read;
}

write

열린 파일의 데이터를 기록

구현 전 힌트

  • 파일에 동시 접근이 일어날 수 있으므로 Lock 사용
  • 파일 디스크립터를 이용하여 파일 객체 검색
  • 파일 디스크립터가 1일 경우 버퍼에 저장된 값을 화면에 출력
    후 버퍼의 크기 리턴 (putbuf() 이용)
  • 파일 디스크립터가 1이 아닐 경우 버퍼에 저장된 데이터를 크기
    만큼 파일에 기록후 기록한 바이트 수를 리턴
int
write(int fd, void *buffer, unsigned size)
{
	// 1. 주소 유효성 검사
	check_address(buffer);
	lock_acquire(&filesys_lock); // 파일에 동시 접근이 일어날 수 있으므로 lock 사용
	struct thread *curr = thread_current();
	
    // fd와 버퍼 유효성 검사
	if (fd < 1 || fd >= FD_MAX || buffer == NULL)
	{
		lock_release(&filesys_lock);
		return -1;
	}
	
    // 5. 표준 입력 처리
	if (fd == 1)
	{ 
		putbuf((char *)buffer, size);
		lock_release(&filesys_lock);

		return size;
	}
	
	// 6. 파일 객체 가져오기
	struct file *f = curr->fd_table[fd];

	if (f == NULL)
	{
		lock_release(&filesys_lock);
		return -1;
	}
	
    // 7. 파일에 데이터 쓰기
	int bytes_written = file_write(f, buffer, size);
    
    // 8. 파일 시스템 락 해제
	lock_release(&filesys_lock);
	return bytes_written;
}

seek

열린 파일의 위치 이동

구현 전 힌트

  • 파일 디스크립터를 이용하여 파일 객체 검색
  • 해당 열린 파일의 위치(offset)를 position만큼 이동
void 
seek(int fd, unsigned position)
{
	if (fd < 0 || fd >= FD_MAX)
	{
		return;
	}

	struct thread *curr = thread_current();
	struct file *f = curr->fd_table[fd];

	if (f != NULL)
	{
		file_seek(f, position);
	}
}

tell

열린 파일의 위치(offset) 알려줌

구현 전 힌트

  • 파일 디스크립터를 이용하여 파일 객체 검색
  • 해당 열린 파일의 위치를 반환
unsigned
tell(int fd)
{
	if (fd < 0 || fd >= FD_MAX)
	{
		return;
	}

	struct thread *curr = thread_current();
	struct file *f = curr->fd_table[fd];

	if (f != NULL)
	{
		return file_tell(f);
	}
}

close

열린 파일을 닫기

구현 전 힌트

  • 해당 파일 디스크립터에 해당하는 파일을 닫음
  • 파일 디스크립터 엔트리 초기화
close(int fd)
{
	process_close_file(fd);
}

최종 시스템 콜 핸들러

void syscall_handler(struct intr_frame *f UNUSED)
{
	int syscall_number = f->R.rax; 

	switch (syscall_number)
	{
	case SYS_HALT:
		halt();
		break;

	case SYS_EXIT:
		exit(f->R.rdi);
		break;

	case SYS_FORK:
		f->R.rax = fork(f->R.rdi);
		break;

	case SYS_EXEC:
		f->R.rax = exec(f->R.rdi);
		break;

	case SYS_WAIT:
		wait(f->R.rdi);
		break;

	case SYS_CREATE:
		f->R.rax = create(f->R.rdi, f->R.rsi);
		break;

	case SYS_REMOVE:
		f->R.rax = remove(f->R.rdi);
		break;

	case SYS_OPEN:
		f->R.rax = open(f->R.rdi);
		break;

	case SYS_FILESIZE:
		f->R.rax = filesize(f->R.rdi);
		break;

	case SYS_READ:
		f->R.rax = read(f->R.rdi, f->R.rsi, f->R.rdx);
		break;

	case SYS_WRITE:
		f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
		break;

	case SYS_SEEK:
		seek(f->R.rdi, f->R.rsi);
		break;

	case SYS_TELL:
		f->R.rax = tell(f->R.rdi);
		break;

	case SYS_CLOSE:
		close(f->R.rdi);
		break;

	default:
		thread_exit();
		break;
	}
}

그 외 함수들

구조체 (thread) - 수정

프로세스 디스크립터에 파일 디스크립터 테이블 추가

// thread.h 파일에
// struct thread{ } 여기에 추가

struct file *fd_table[FD_MAX];
int max_fd;

자료구조 - 수정

스레드 생성 시 파일 디스크립터 초기화

// thread.c 파일에
// init_thread 함수에 추가

t->fd_table[0] = 0;
t->fd_table[1] = 1;
t->max_fd = 2; //

process_add_file

파일 객체를 파일 디스크립터 테이블에 추가

구현 전 힌트

  • 파일 객체를 파일 디스크립터 테이블에 추가
  • 파일 디스크립터의 최대값 1 증가
  • 파일 디스크립터 리턴
int process_add_file(struct file *f)
{
    struct thread *cur = thread_current();
    
    // 새로운 파일 디스크립터 할당(가장 큰 파일 디스크립터 번호)
    int fd = cur->max_fd;
	
    // 파일 디스크립터 테이블 크기 확인.
    if (fd >= FD_MAX)
        return -1; // 파일 디스크립터 테이블이 꽉 찬 경우
	
    // 파일 디스크립터 테이블에 파일 포인터 추가.
    cur->fd_table[fd] = f;
    
    // 파일 디스크립터 번호 증가
    cur->max_fd++;
    return fd;
}

process_get_file

파일 객체 검색

구현 전 힌트

  • 파일 디스크립터에 해당하는 파일 객체를 리턴
  • 없을 시 NULL 리턴
struct
file *process_get_file(int fd)
{
	struct thread *t = thread_current();
	if (2 <= fd < FD_MAX)
	{
		if (t->fd_table[fd] != NULL)
		{
			return t->fd_table[fd];
		}
	}
	return NULL;
}

process_close_file

파일 디스크립터에 해당하는 파일을 닫고 해당 엔트리 초기화

구현 전 힌트

  • 파일 디스크립터에 해당하는 파일을 닫음
  • 파일 디스크립터 테이블 해당 엔트리 초기화
void process_close_file(int fd)
{
	struct thread *t = thread_current();

	if (2 <= fd < FD_MAX)
	{
    	
        // 파일 닫기
		file_close(t->fd_table[fd]);
        
        // 엔트리 제거
		t->fd_table[fd] = NULL;	
	}
}

process_exit - 수정

모든 열린 파일 닫기

구현 전 힌트

  • 프로세스에 열린 모든 파일을 닫음
  • 파일 디스크립터 테이블의 최대값을 이용해 파일 디스크립터
    의 최소값인 2가 될 때까지 파일을 닫음
  • 파일 디스크립터 테이블 메모리 해제
void
process_exit(void)
{

struct thread *curr = thread_current ();
	
    // 열린 파일들 닫기
	for(int fd = 2; fd < FD_MAX; fd++)
	{
		process_close_file(fd);
	}
	
    // 프로세스와 관련된 자원 정리하는 함수 호출
	process_cleanup();
	
    // 스레드의 종료를 알리기 위해 세마포어 업
	sema_up(&thread_current()->sema_exit);
}

0개의 댓글