Project 2 - User Programs

qwbsy·2021년 12월 17일
0

pintOS 운영체제는 사용자가 프로그램을 사용할 수 있도록 컨트롤해줘야 한다. 그렇다고 모든 부분을 사용자가 컨트롤할 수 있게 하면 문제가 생길 수 있기 때문에 그 부분도 미리 방지해두어야 한다. 이번 프로젝트에서 관련 내용들을 살펴보자

Argument Passing

우리가 cmd, 터미널 등에서 명령어를 입력하는 것처럼 pintOS에도 명령어가 들어올텐데 우선 그걸 해석해야 어떻게 실행할지도 정할 수 있을 것이다. 명령어들을 띄어쓰기 단위로 토큰화해서 argument stack에 쌓는데 word-align도 해주고 끝을 알려주는 NULL포인터도 쌓고 각 토큰들의 주소값도 쌓는데 정확한 개념은 잘 모르겠다. 추가로 공부해보도록 하자.

User Memory Access

유저가 잘못된 포인터를 사용하거나 커널 메모리를 가리키려고 하는 등 허용되지 않은 메모리를 접근하려고 할 때 컨트롤해줘야 한다.

System Calls

user가 허용된 범위만 접근할 수 있다면 실제로 할 수 있는 것이 매우 적은데 이를 해결하기 위해 system call로 커널이 대신 수행해주도록 해야 한다. halt, exit, fork, exec, open, read, write 등의 행위를 커널이 관리해서 작동하도록 코드를 작성한다.

Process Termination Message

프로세스가 종료되면 메세지를 출력하라는 내용이다. 종료되는 부분에 printf 를 넣어주면 된다.

Deny Write on Executables

실행 중인 파일은 write 기능을 제한하라는 내용이다. 파일이 오픈될 때, file_deny_write 함수를 활용해서 해당 파일 구조체의 deny_write 값을 true 로 만들어주고 inode의 deny_write_count 값을 올려준다. 해당 쓰레드에서 어떤 파일이 실행되고 있는지 알기 위해서 쓰레드 구조체의 running에 해당 파일을 넣어준다. 프로세스가 종료되거나 close 가 진행될 때 file_allow_write로 반대 행위를 한다. 코드에서는 file_write 함수 안에 inode_write_at 함수로 inode의 deny_write_count 값이 있으면 return 하게 되어 있다.

Extend File Descriptor(Extra)

Project2 의 추가 과제로 특정 식별자가 이미 열려 있는 파일을 가리키도록 하는 함수를 구현하는 것이다. 같은 파일을 연다고 해도 열고 나면 각각의 공간에서 프로세스가 진행되기 때문에 이를 동기화해주는 역할이라고 본다.

int dup2(int oldfd, int newfd) {
	struct file *file_fd = find_file_by_fd(oldfd);
	struct thread *cur = thread_current();
	struct file **fdt = cur->fd_table;

	if (file_fd == NULL)
		return -1;

	if (oldfd == newfd)
		return newfd;

	if (file_fd == 1)
		cur->stdin_count++;
	else if (file_fd == 2)
		cur->stdout_count++;
	else
		file_fd->dupCount++;
	
	close(newfd);
	fdt[newfd] = file_fd;
	return newfd;
}

없는 파일을 가리키려고 하면 -1 을 return하고 같은 식별자를 인자로 받으면 무의미한 행위여서 해당 식별자값을 return하고 그 외에 경우에 실제 dup2함수가 실행된다. stdin과 stdout은 프로세스 실행시 생성되는 기본 식별자여서 해당 쓰레드 구조체에 복제횟수를 관리하고, 이외의 파일들은 각 파일 구조체에 복제횟수를 관리한다. 여기서는 dup2이 정상 실행되면 플러스를 해주는데 그럼 어디에 저 값이 처음으로 지정되는지를 확인해야 한다. stdin과 stdout의 count 값은 쓰레드 구조체에 있으니 쓰레드가 처음 생성될 때 1로 초기화해주면 되고, 다른 파일의 dupCount는 파일이 생성될 때 0으로 초기화해준다(복제될 땐 해당 파일의 dupCount를 적용).
그 이후에 newfd가 열어놓은 파일은 쓸모가 없어지니 close를 진행하고 쓰레드가 저장하고 있는 식별자테이블에 newfd가 가리키게 된 파일을 저장해준다.

그런데 기존 상태로는 이러한 복제행위(dup2)가 fork로 추가되는 프로세스에는 반영되지 않는다. 이 부분을 해결해주기 위해 __do_fork 함수를 수정한다.

struct MapElem
{
	uintptr_t key;
	uintptr_t value;
};
static void __do_fork (void *aux)
	...
	const int MAPLEN = 10;
	struct MapElem map[10]; // key - parent's struct file * , value - child's newly created struct file *
	int dup_count = 0;		// index for filling map

	for (int i = 0; i < FDCOUNT_LIMIT; i++)
	{
		struct file *file = parent->fd_table[i];
		if (file == NULL)
			continue;

		// Project2-extra) linear search on key-pair array
		// If 'file' is already duplicated in child, don't duplicate again but share it
		bool found = false;
		for (int j = 0; j < MAPLEN; j++)
		{
			if (map[j].key == file)
			{
				found = true;
				current->fd_table[i] = map[j].value;
				break;
			}
		}
		if (!found)
		{
			struct file *new_file;
			if (file > 2)
				new_file = file_duplicate(file);
			else
				new_file = file; // 1 STDIN, 2 STDOUT

			current->fd_table[i] = new_file;
			if (dup_count < MAPLEN)
			{
				map[dup_count].key = file;
				map[dup_count++].value = new_file;
			}
		}
	}
	...

dup2() 함수가 실행되지 않은 프로세스를 fork 하면 if(!found) 안의 내용만으로 파일 복사가 되는데 dup2() 함수가 적용된 파일이 있으면 그 부분을 fork 된 프로세스에도 적용하기 위한 작업이 필요하다.

0개의 댓글