rox 테스트는 무엇일까?
먼저 과제 설명서를 보자

친절하게 설명되어있지만, 잘 모르겠으면 테스트 파일을 까보면 된다.
rox 테스트 알아보기
rox-simple.c
/* 실행 중인 프로세스의 실행 파일이 수정될 수 없음을 보장한다. */
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
void test_main(void)
{
int handle;
char buffer[16];
CHECK((handle = open("rox-simple")) > 1, "open \"rox-simple\"");
CHECK(read(handle, buffer, sizeof buffer) == (int)sizeof buffer,
"read \"rox-simple\"");
CHECK(write(handle, buffer, sizeof buffer) == 0,
"try to write \"rox-simple\"");
}
이 파일이 유저 프로그램으로 실행되면, rox-simple을 또 open한다. 유저 프로그램이 자기 자신의 파일을 open하는 것이다. 그 이후 write를 시도한다. 우리는 write를 못하도록 구현해줘야 한다.
자신의 파일에 write를 못하도록 막으려면 어떻게 할까? file마다 bool 변수 같은것을 둬서 확인한 다음 막아주면 되지 않을까? 이러한 로직이 이미 pintos 내에는 구현되어 있다. 사실 굉장히 친절한 과제인 것 같다.
필요한 함수 알아보기
file_deny_write ( struct file * )
/* Prevents write operations on FILE's underlying inode
* until file_allow_write() is called or FILE is closed. */
void
file_deny_write (struct file *file) {
ASSERT (file != NULL);
if (!file->deny_write) {
file->deny_write = true;
inode_deny_write (file->inode);
}
}
우리는 이 함수를 통해 쓰기를 차단할 수 있다 !!
deny_write는 file 구조체 내에 선언된 bool 변수이다.
이걸 통해 어떻게 write를 막을 수 있다는 걸까?
먼저 inode_deny_write를 보자
inode_deny_write( struct inode * )
void
inode_deny_write (struct inode *inode)
{
inode->deny_write_cnt++;
ASSERT (inode->deny_write_cnt <= inode->open_cnt);
}
인자로 받은 inode의 deny_write_cnt를 증가시킨다
이건 어디에 쓰는 필드인가?
그건 file.c에 구현되어있는 file_write를 봐야 알 수 있다.
file_write ( struct *file, const void *, off_t )
off_t
file_write (struct file *file, const void *buffer, off_t size) {
off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos);
file->pos += bytes_written;
return bytes_written;
}
여기서 내부적으로 또 inode_write_at을 사용한다
inode_write_at ( struct inode *, const void *, off_t )
off_t
inode_write_at (struct inode *inode, const void *buffer_, off_t size,
off_t offset) {
const uint8_t *buffer = buffer_;
off_t bytes_written = 0;
uint8_t *bounce = NULL;
if (inode->deny_write_cnt)
return 0;
while (size > 0) {
/* Sector to write, starting byte offset within sector. */
disk_sector_t sector_idx = byte_to_sector (inode, offset);
int sector_ofs = offset % DISK_SECTOR_SIZE;
...
이 함수 내에서 inode의 deny_write_cnt가 1 이상인지 검사하고, 그렇다면 0을 리턴한다. 0이라면 다음 로직으로 넘어가 실제 write를 수행한다
0이 반환된다면 file_write에서는 전달받은 0을 다시 리턴하고, 결과적으로 아무것도 파일에 write 되지 않는 것이다
그럼 file_deny_write를 사용하기 위해 필요한 것은 무엇일까?
file_deny_write의 인자는 struct *file 하나 뿐이었다
그럼 load 함수 내에서 file을 open할 때 바로 file_deny_write를 걸어주면 되는걸까?
static bool
load(const char *file_name, struct intr_frame *if_)
{
/* 실행 파일을 엽니다. */
file = filesys_open(file_name);
file_deny_write(file);
if (file == NULL)
{
printf("load: %s: open failed\n", file_name);
goto done;
}
...
이렇게 말이다. 물론 여기서 추가하면 마지막에 file_close가 되기 때문에 말짱 도루묵이다
file_close를 주석해주면 되는거 아닌가? 싶지만... load가 실행 도중 예상치 못하게 종료되면? 열린 파일은 어떻게 종료시킬것인가?
또한 실행이 끝난 다음 이 파일의 쓰기 금지를 풀어줘야 할 텐데 그때는 어떻게 풀어줄 것인가? 따라서 우리는 어딘가에 실행중인 파일을 추적해 둘 필요가 있다. 그리고 그곳은 thread 구조체가 가장 적합해보인다.
구현 및 수정
struct thread 수정
struct thread
{
...
struct file *running_file;
...
}
현재 쓰레드의 파일이 무엇인지 나타낼 running_file 필드를 만들어두자
process_exec 수정
int process_exec(void *f_name)
{
...
palloc_free_page(file_name);
if (!success)
return -1;
/* 추가한 부분 */
thread_current()->running_file = filesys_open(cp_file_name);
file_deny_write(thread_current()->running_file);
/* 여기까지 */
...
}
running_file에 로드된 실행 파일을 열어서 집어넣자. 그리고 file_deny_write를 사용하자
process_exit 수정
void process_exit(void)
{
...
if (curr->running_file != NULL)
{
file_allow_write(curr->running_file);
file_close(curr->running_file);
}
...
}
이제 exit 할 때는위 순서의 반대로 해주면 된다 !!
하지만 이 코드에는 옥의 티가 있다. 사실 file_close 내에는 file_allow_write가 이미 있기 때문에 지금 추가해둘 필요가 없다 !!
그럼 file_allow_write가 뭐길래 쓰기를 허용해주는 걸까?
file_allow_write 따라가기
void
file_allow_write (struct file *file) {
ASSERT (file != NULL);
if (file->deny_write) {
file->deny_write = false;
inode_allow_write (file->inode);
}
}
void
inode_allow_write (struct inode *inode) {
ASSERT (inode->deny_write_cnt > 0);
ASSERT (inode->deny_write_cnt <= inode->open_cnt);
inode->deny_write_cnt--;
}
인자로 받은 파일의 deny_write가 true라면 false로 바꿔준 다음, inode_allow_wirte를 호출해준다.
inode_allow_write는 file 구조체의 inode의 deny_write_cnt를 1 감소시켜준다
따라서 위에서 봤던 inode_write_at에서 deny_write_cnt가 1 이상인지 검사하는 if 문에서 걸리지 않기 때문에 write가 가능해지는 것이다 !!
사실 코드 네줄이면 완성되는 간단한 로직이다. 로직을 구현하는 것 보다 pintOS에서 어떻게 이 로직을 구현하는게 가능한지, 내부 함수가 어떤 일들을 하는지 알아보는게 더 재밌었다.
'크래프톤 정글' 카테고리의 다른 글
| [pintOS] frame와 page, SPT와 프레임 테이블 (0) | 2025.05.26 |
|---|---|
| [pintOS] dup2 구현기 (0) | 2025.05.24 |
| [pintOS] 시스템 콜 wait 구현기 (1) | 2025.05.21 |
| [pintOS] ubuntu 22.04에서 로드 버그가 발생할 때 (0) | 2025.05.21 |
| [pintOS] 시스템 콜 fork 구현기 (0) | 2025.05.19 |