void halt(void)
✅void exit (int status);
✅bool create (const char *file, unsigned initial_size);
✅bool remove (const char *file);
✅int open (const char *file);
int filesize (int fd);
int read (int fd, void *buffer, unsigned size);
int write (int fd, const void *buffer, unsigned size);
void close (int fd);
void seek (int fd, unsigned position);
unsigned tell (int fd);
int exec (const char *cmd_line);
int wait (pid_t pid);
tid_t fork(const char *thread_name, struct intr_frame *f);
저번에 했던 것들 이후에 open부터 시작해보겠다.
그 전에 open을 구현할때 필요한 check_address를 설명하고 시작해보자면…
void check_address(void *addr)
void check_address(void *addr)
{
struct thread *t = thread_current();
if (is_kernel_vaddr(addr) || addr == NULL || pml4_get_page(t->pml4, addr) == NULL)
// if (addr == NULL || is_kernel_vaddr(addr) || pml4e_walk(t->pml4, addr, false) == NULL)
exit(-1);
}
check_address
함수는 주소의 유효성을 체크라는 함수이다.
포인터가 가리키는 주소가 유저 영역의 주소인지 확인하고, 잘못된 접근일 경우 프로세스를 종료한다.
int open (const char *file);
int open(const char *file)
{
check_address(file);
struct thread *t = thread_current();
struct file **file_descriptor_table = t->file_descriptor_table;
int fd = t->fd_number;
/* 파일을 open */
struct file *f = filesys_open(file);
// 해당 파일이 존재하지 않으면 -1 리턴
if (f == NULL)
return -1;
while (t->file_descriptor_table[fd] != NULL && fd < FDT_COUNT_LIMIT)
fd++;
if (fd >= FDT_COUNT_LIMIT)
file_close(f);
t->fd_number = fd;
file_descriptor_table[fd] = f;
// 파일 디스크립터 리턴
return fd;
}
Opens the file called file. Returns a nonnegative integer handle called a "file descriptor" (fd), or -1 if the file could not be opened. File descriptors numbered 0 and 1 are reserved for the console: fd 0 (STDIN_FILENO) is standard input, fd 1 (STDOUT_FILENO) is standard output. The open system call will never return either of these file descriptors, which are valid as system call arguments only as explicitly described below. Each process has an independent set of file descriptors. File descriptors are inherited by child processes. When a single file is opened more than once, whether by a single process or different processes, each open returns a new file descriptor. Different file descriptors for a single file are closed independently in separate calls to close and they do not share a file position. You should follow the linux scheme, which returns integer starting from zero, to do the extra.
open()
시스템 콜은 파일을 열때 사용하는 시스템 콜이다.
성공시 fd를 생성하고 반환한다. 그리고 실패시 -1
을 반환한다. 인자 file
은 파일의 이름 및 경로 정보이다.
파일 디스크립터의 내용을 참조하면 좋다. file_descriptor_table
은 파일 디스크립터들이 모여있는 테이블이다. 여기 안에 파일 디스크립터의 인덱스가 있다. filesys_open
함수를 통해 파일을 연다.
그리고 프로세스가 실행중인 파일을 open
하면 커널은 해당 프로세스의 파일 디스크립터 숫자 중에 사용하지 않는 가장 작은 값을 할당해주어야한다. 그래서 while
문을 통해 순회를 하며 fd
의 숫자를 1씩 증가시켜준다.
실패시 -1을 반환해주고, fd
가 LIMIT
보다 커지면 파일을 종료시켜주는 등의 예외 처리를 해준다.
최종적으로 fd
를 반환해준다
int filesize (int fd);
int filesize(int fd)
{
struct file *f = get_file(fd);
if (f == NULL)
return -1;
int result = file_length(f);
return result;
}
Returns the size, in bytes, of the file open as
fd
.
filesize
시스템 콜은 파일의 크기를 알려주는 시스템 콜이다. 성공시 파일의 크기를 반환하고, 실패시 -1을 반환한다.
여기서도 file.c
에 있는 file_length()
함수를 사용한다면 쉽게 구현할 수 있다.
struct file *fd_to_file(int fd)
struct file *fd_to_file(int fd)
{
if (fd < 0 || fd >= FDT_COUNT_LIMIT || fd == NULL)
return NULL;
struct thread *t = thread_current();
struct file *f;
f = t->file_descriptor_table[fd];
return f;
}
파일디스크립터를 이용하여 파일 객체를 검색하는 함수이다.
여러 시스템콜에서 쓰일것이어서 미리 함수를 만들어놓았다.
@/include/userprog/syscall.h
struct lock filesys_lock;
@/userprog/syscall.c
void syscall_init (void) {
(...)
lock_init(&filesys_lock);
}
int read (int fd, void *buffer, unsigned size);
int read(int fd, void *buffer, unsigned size)
{
struct file *f = fd_to_file(fd);
check_address(buffer);
check_address(buffer + size - 1);
unsigned char *buf = buffer;
char key;
if (f == NULL)
return -1;
int i = 0;
if (size == 0)
return 0;
if (fd == 0)
{
while (i <= size)
{
key = input_getc();
buf = key;
buffer++;
if (key == "\0")
break;
i++;
}
}
else if (fd == 1)
return -1;
lock_acquire(&filesys_lock);
i = file_read(f, buffer, size);
lock_release(&filesys_lock);
return i;
}
Reads
size
bytes from the file open asfd
intobuffer
. Returns the number of bytes actually read (0
at end of file), or-1
if the file could not be read (due to a condition other than end of file).fd
0 reads from the keyboard usinginput_getc()
.
read
는 열린 파일의 데이터를 읽는 시스템 콜이다. 성공 시 읽은 바이트 수를 반환하고, 실패시 -1을 반환한다. fd 값이 0일때 키보드의 데이터를 읽어 버퍼에 저장한다.
인자는 다음과 같다.
buffer
: 읽은 데이터를 저장할 버퍼의 주소 값
size
: 읽을 데이터 크기
공식 문서의 설명처럼 fd
가 0일때는 input_getc()
함수를 사용해야한다. 이 함수는 키보드에 입력을 하나씩 버퍼에 저장한 수 버퍼의 저장한 크기를 리턴해주는 함수이다. 여기서 중요한것은 하나씩이라는 점이다. 그래서 반복문을 통해 모든 입력을 저장하도록 해준다. fd
가 1일때도 return -1
을 통해 종료를 시켜주어야한다.
그리고 파일이 동시에 접근이 일어날수있으므로 lock
을 이용해야한다. lock
으로 묶고 file_read
함수를 이용해서 시스템 콜을 완성한다.
int write(int fd, const void *buffer, unsigned size)
int write(int fd, const void *buffer, unsigned size)
{
struct file *f = fd_to_file(fd);
if (fd == 0)
return 0;
check_address(buffer);
if (fd == 1)
{
putbuf(buffer, size);
return size;
}
if (f == NULL)
return 0;
if (size == 0)
return 0;
lock_acquire(&filesys_lock);
int i = file_write(f, buffer, size);
lock_release(&filesys_lock);
return i;
}
Writes
size
bytes frombuffer
to the open filefd
. Returns the number of bytes actually written, which may be less thansize
if some bytes could not be written. Writing past end-of-file would normally extend the file, but file growth is not implemented by the basic file system. The expected behavior is to write as many bytes as possible up to end-of-file and return the actual number written, or0
if no bytes could be written at all.fd
1 writes to the console. Your code to write to the console should write all of buffer in one call toputbuf()
, at least as long as size is not bigger than a few hundred bytes (It is reasonable to break up larger buffers). Otherwise, lines of text output by different processes may end up interleaved on the console, confusing both human readers and our grading scripts.
write
시스템콜은 열린 파일의 데이터를 기록하는 시스템 콜이다.
성공시 기록한 데이터의 바이트 수를 반환하고, 실패시 -1을 반환한다. fd
값이 1이면 버터에 저장된 데이터를 화면에 출력한다. 이는 putbuf()
함수를 사용한다.
read
와 마찬가지로 동시 접근이 일어날수 있으므로 write
도 lock
을 이용해서 보호를 해준다. 그리고 file_write()
함수를 이용해서 버퍼에 저장된 데이터를 크기만큼 파일에 기록후 기록한 바이트 수를 반환한다.
void close (int fd);
void close(int fd)
{
struct thread *t = thread_current();
struct file *f = fd_to_file(fd);
if (f == NULL)
return;
lock_acquire(&filesys_lock);
file_close(f);
lock_release(&filesys_lock);
t->file_descriptor_table[fd] = NULL;
}
Closes file descriptor
fd
. Exiting or terminating a process implicitly closes all its open file descriptors, as if by calling this function for each one.
close
시스템콜은 열린 파일을 닫는 시스템콜이다. file_close
함수를 이용해서 닫는다. close
도 마찬가지로 lock
을 통해서 동시접근으로 인한 오류를 막는다.
void seek(int fd, unsigned position)
void seek(int fd, unsigned position)
{
if (fd < 2)
return;
struct file *f = fd_to_file(fd);
if (f == NULL)
return;
file_seek(f, position);
}
seek
시스템 콜은 열린 파일의 위치를 이동하는 시스템콜이다. file_seek
함수를 이용하면 쉽게 구현이 가능하다. 그 외의 예외 처리도 해주어야한다.
unsigned tell(int fd)
unsigned tell(int fd)
{
if (fd < 2)
return;
struct file *f = fd_to_file(fd);
if (f == NULL)
return;
check_address(f);
return file_tell(f);
}
tell
시스텀 콜은 열린 파일의 위치를 알려주는 시스템 콜이다. file_tell
함수를 이용하여 쉽게 구현가능하다.