크래프톤 정글 TIL : 0909

lazyArtisan·2024년 9월 9일
0

정글 TIL

목록 보기
71/147

📚 Journal


오늘 월요일인데 벌써 거의 다 한 동기(이 친구 답지도 안 봄)가 있길래 어떻게 했는지 복기해달라고 함

  • 깃북의 요구 사항을 최대한 잘 읽은듯
  • 먼저 introduction에 있는 함수들 쭉 봄 (하루 걸림, 완벽히 이해한 건 아님)
  • 문제에 접근할 땐 일단 구현돼있는게 있는지 먼저 검색해봄
  • 실제로는 어떻게 구현돼있는지 gpt한테 물어보기도
  • 자체가 머리가 좋은 것 (+ 공부할 때 제대로 함) 도 있는듯 : 저번에 csapp 공부한 걸로 rip가 다음 인스트럭션 실행하는 거 기억하고 있었다고 함, 앞에서 봤던 함수에서 이런게 있어서~ 라고도 함.
  • 책도 보긴 함 : fork할 때 개념을 익히려고 OSTEP 봄
  • 메모하면서 하진 않는다 : 이건 못 따라하나? 머릿속에 무엇을 해야하는지에 대해 길을 잃지 않고 모든 context를 담고 있는듯.
  • 기록을 남기지는 않는다 : 그로 인한 오버헤드 x
  • 오후 12시 - 1시에 와서 새벽 1시 30분 쯤 갔다 : 시간은 대충 나랑 비슷함
  • 근데 이 친구 질문 많이 받아서 이리저리 불려다니긴 했음

복기 좀 해달라고 해서 다 들어봤는데 요약하면 단서를 잡고 해결책을 찾아가는 과정의 반복.

내가 했던 쓸모없는 짓

  • 깃북에 있던 것들을 "굳이" 벨로그에 옮김
  • 카이스트 강의 굳이 하나하나 스샷 따면서 번역함 : 필요가 없는 짓이었음. 깃북과 코드와 책만 보고 하는게 가능했다.
  • 세세한거 일일이 하나하나 벨로그에 적음: TIL에 적을 걸 한정시켜야 함. 의미있는 개념만 정리하면 될 것 같은데 너무 많이 적음. 트러블 슈팅은 간단하게 한 줄 내지 두 줄로 요약하는것이 좋을듯.

기록의 의미가 있긴 함. 내가 나중에 보고 복습할 수 있음.
근데 "의미있는" 기록을 알차게 남겨야 함.
딱히 의미 없는 것 같으면 기록 전부 다 거르기.

텍스트를 정독하는 능력 : 대충대충 읽지 말아야 함. 하나하나 꼼꼼히. 마음 속에 조급함이 있어서?
해당 문제에 집중하는 능력 : 내가 이것을 왜 해야되는지에 대한 동기부여?

어떤 것을 읽고 대충 말고 완벽히 이해하는 연습을 꾸준히 하면 됨. 지금까지의 인생은 어떻게든 미뤄왔으니 어쩔 수 없고, 지금부터라도 뇌의 구조를 바꾸려고 노력하면 됨.

흐리멍텅하게 생각하면 안된다는 이미지 각인

느리보이고 답답하더라도 필요한 것을 천천히 정독해야 오히려 빠르고 멀리 갈 수 있다.



📝 배운 것들


🏷️ directory와 inode

아이노드

  • 파일 시스템에서 파일 메타데이터를 저장하는 데이터 구조
  • 각 파일이나 디렉토리는 고유한 아이노드 번호를 가짐
  • 아이노드에 저장되는 정보:
    • 파일의 크기
    • 파일의 소유자 및 그룹
    • 파일의 접근 권한(읽기, 쓰기, 실행)
    • 파일이 마지막으로 수정된 시간
    • 파일이 저장된 블록의 물리적 위치
  • 아이노드에는 파일 이름 없음. 파일 이름은 디렉토리 엔트리에서 관리됨.

디렉토리

  • 파일 시스템에서 파일을 그룹으로 관리하는 구조
  • 디렉토리는 특수한 종류의 파일이라 할 수 있음
  • 파일 이름과 그에 대응하는 아이노드 번호를 저장하고 있음
  • 디렉토리의 역할:
    • 파일 이름과 아이노드 번호를 매핑하는 테이블 관리
    • 디렉토리 엔트리는 사용자가 파일을 이름으로 쉽게 찾을 수 있게 함

디렉토리와 아이노드의 관계

  • 디렉토리는 파일 이름을 관리하고, 파일 이름과 아이노드 번호를 연결함.
  • 사용자가 파일 이름으로 파일 열거나 작업할 때,
    • 파일 시스템은 디렉토리에서 파일 이름을 찾고,
    • 그 이름에 연결된 아이노드 번호를 통해 파일에 대한 정보를 가져옴.
  • 파일 이름이 변경되더라도, 해당 파일의 아이노드 번호는 그대로 유지됨.

디렉토리와 아이노드의 작동 과정

  1. 파일 생성:

    • 파일을 생성할 때, 파일 시스템은 새로운 아이노드를 할당하고, 해당 아이노드에 파일의 메타데이터를 기록합니다.
    • 디렉토리 엔트리에는 파일 이름과 함께 할당된 아이노드 번호가 저장됩니다.
  2. 파일 접근:

    • 사용자가 파일을 열면, 운영체제는 먼저 디렉토리에서 파일 이름을 검색합니다.
    • 파일 이름이 발견되면, 해당 파일 이름에 대응하는 아이노드 번호를 찾고, 아이노드를 통해 파일의 메타데이터 및 실제 데이터에 접근하게 됩니다.
  3. 파일 삭제:

    • 파일이 삭제될 때는 디렉토리에서 파일 이름과 아이노드 번호에 대한 매핑이 제거됩니다.
    • 아이노드는 더 이상 참조되지 않으면, 해당 파일이 사용하던 블록을 포함한 모든 리소스가 해제됩니다.

🏷️ c에서 배열 선언할 때 vm 위치

스택: 함수 내에서 선언된 로컬 배열
힙: malloc() 같은 동적 할당 함수로 생성된 배열
데이터 영역: 전역 또는 정적 변수로 선언된 배열

페이지 테이블 엔트리

페이지 테이블 엔트리(Page Table Entry, PTE)는 컴퓨터 운영 체제에서 가상 메모리를 물리 메모리로 매핑하는 중요한 역할을 합니다. 운영 체제는 페이지 테이블을 통해 가상 주소 공간과 실제 물리 메모리 주소를 연결하는데, 페이지 테이블 엔트리는 그 중에서 개별 페이지(일정 크기의 메모리 블록)의 매핑 정보를 담고 있는 항목입니다.

각 페이지 테이블 엔트리는 다음과 같은 정보를 포함할 수 있습니다:

  1. 물리 메모리 주소: 가상 메모리의 특정 페이지가 실제로 물리 메모리의 어느 위치에 저장되어 있는지를 나타냅니다.
  2. 상태 비트(플래그): 페이지의 상태를 나타내는 여러 비트들로, 대표적으로 다음이 포함됩니다.
    • 유효 비트(Valid bit): 해당 페이지가 현재 물리 메모리에 있는지 여부를 나타냅니다. 유효하지 않다면 페이지 폴트가 발생할 수 있습니다.
    • 읽기/쓰기 비트(Read/Write bit): 페이지가 읽기 전용인지, 아니면 쓰기 가능한지를 나타냅니다.
    • 사용자/커널 비트(User/Supervisor bit): 해당 페이지가 사용자 모드에서 접근 가능한지, 아니면 커널 모드에서만 접근 가능한지를 나타냅니다.
    • 참조 비트(Accessed bit): 해당 페이지가 최근에 참조되었는지 나타내며, 메모리 관리 정책에서 자주 사용됩니다.
    • 수정 비트(Dirty bit): 페이지의 내용이 수정되었는지, 즉 페이지가 메모리에서 디스크로 다시 써야 하는지를 나타냅니다.


🖥️ PintOS

🔷 System Calls


file descriptor table

filesys.c에 있는 함수 중 file과 직접적으로 관련된 함수는 filesys_open 하나

그것은 파일의 이름이 인자로 주어지면 directory.c를 이용해서 inode를 찾은 뒤
그 inode를 인자로 넘겨주며 file.c에 있는 file_open()을 호출한다.

그러면 file 구조체가 동적 할당되며 초기화된다.
해당 파일 구조체 자체가 반환됨.

지금 내가 해야될거: 파일을 열 때 파일 디스크립터 테이블에 파일 구조체를 할당한다

int find_next_fd(struct thread *t)
{
	struct file **fdt = t->fdt;

	for (int i = 2; i < 64; i++)
	{
		if (fdt[i] == NULL)
		{
			t->next_fd = i;
			return i;
		}
	}
	return -1; // 64개나 채울 일이? 일단 처리는 해둠.
}

/* Opens a file for the given INODE, of which it takes ownership,
 * and returns the new file.  Returns a null pointer if an
 * allocation fails or if INODE is null. */
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;

		// 파일 디스크립터 테이블에 해당 파일을 추가해야 함
		struct thread *t = thread_current();
		if (t->fdt != NULL)
		{
			struct file **fdt = t->fdt;
			fdt[t->next_fd] = file;
			find_next_fd(t);
		}

		return file;
	}
	else
	{
		inode_close(inode);
		free(file);
		return NULL;
	}
}

이론상 되어야 하긴 함

되는듯?

이제 해야될 거 : 파일을 닫을 때 파일 디스크립터에서 해당 파일 구조체를 지운다.

그래서 close 구현하려고 봤더니 애초에 open에서부터 연 파일의 fd를 반환해야함.
그래야 close의 인자로 open의 반환값 fd가 전해짐.

그런데 file_open과 filesys_open은 int가 아니라 struct *file을 반환함.
이렇게 되면 내가 어떤 fd에 file을 할당했는지 알 수 없음.

즉, file_open과 filesys_open에서 fd를 할당하는 작업을 처리하는게 아니라
그 두 함수를 호출하는 open() 시스템 콜에서 fd를 할당한 뒤
할당한 fd를 반환해야 함.

int find_next_fd(struct thread *t)
{
	struct file **fdt = t->fdt;

	for (int i = 2; i < 64; i++)
	{
		if (fdt[i] == NULL)
		{
			t->next_fd = i;
			printf("할당된 fd: %d", i);
			return i;
		}
	}
	return -1; // 64개나 채울 일이? 일단 처리는 해둠.
}

/* file(첫 번째 인자)이라는 이름을 가진 파일을 엽니다. */
int open(const char *file)
{
	if (file == NULL || pml4_get_page(thread_current()->pml4, file) == NULL || !is_user_vaddr(file))
		exit(-1);

	if (strlen(file) == 0)
		return -1;

	struct file *real_file = filesys_open(file);

	// 파일 디스크립터 테이블에 해당 파일을 추가해야 함
	struct thread *t = thread_current();
	if (t->fdt != NULL)
	{
		struct file **fdt = t->fdt;
		fdt[t->next_fd] = real_file;
		find_next_fd(t);
	}

	return;
}

위치 옮김. 역시 잘 됨.
일단 close 구현하러 감.
fdt 완성하려면 close도 건드려야 됨.

close

/* 파일 식별자 fd를 닫습니다. */
void close(int fd)
{
	// 파일 디스크립터 테이블에서 fd에 할당된 file 구조체를 찾은 뒤에 일단 저장한 뒤
	// 해당 file 구조체를 테이블에서 삭제하고
	// file 구조체로 file_close 호출

	struct thread *t = thread_current();
	struct file **fdt = t->fdt;
	struct file *file = fdt[fd]; // fd에 할당된 file 구조체

	fdt[fd] = NULL; // 테이블에서 삭제

	file_close(file);
}

이렇게 짜고 테스트 돌려봄

pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/close-normal:close-normal -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run close-normal < /dev/null 2> tests/userprog/close-normal.errors > tests/userprog/close-normal.output
perl -I../.. ../../tests/userprog/close-normal.ck tests/userprog/close-normal tests/userprog/close-normal.result
FAIL tests/userprog/close-normal
Run started 'close-normal' but it never finished
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/close-twice:close-twice -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run close-twice < /dev/null 2> tests/userprog/close-twice.errors > tests/userprog/close-twice.output
perl -I../.. ../../tests/userprog/close-twice.ck tests/userprog/close-twice tests/userprog/close-twice.result
FAIL tests/userprog/close-twice
Run started 'close-twice' but it never finished
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/close-bad-fd:close-bad-fd -- -q   -f run close-bad-fd < /dev/null 2> tests/userprog/close-bad-fd.errors > tests/userprog/close-bad-fd.output
perl -I../.. ../../tests/userprog/close-bad-fd.ck tests/userprog/close-bad-fd tests/userprog/close-bad-fd.result
FAIL tests/userprog/close-bad-fd
Run started 'close-bad-fd' but it never finished

통과를 아무것도 못하는 것까지는 그렇다 치는데

open-missing 되살리기

perl -I../.. ../../tests/userprog/open-missing.ck tests/userprog/open-missing tests/userprog/open-missing.result
FAIL tests/userprog/open-missing
run: open() returned 2: FAILED

이것도 망가져버림.

예외처리해서 open-missing 다시 통과.
좀 ad-hoc인가 싶긴 하지만 방법이 없음.

close-normal, close-twice

Executing 'close-normal':
(close-normal) begin
(close-normal) open "sample.txt"
(close-normal) close "sample.txt"
Timer: 314 ticks

그나저나 Run started 'close-normal' but it never finished 이건 왜일까?
결과를 봐도 확실히 exit()가 없다.

할당 해제가 안돼서 그런가? 어제 했는데?

	case SYS_CLOSE:
		halt();
		break;

ㅋㅋㅋㅋㅋㅋㅋ 시스템 콜 핸들러에 close()를 안 써놔서 그랬던 거였음.

고쳤더니 정상 작동. twice까지도 통과하는데 왜 그런건진 모르겠.
어딘가에서 예외 처리에 걸리나봄.

close-bad-ptr

/* 파일 식별자 fd를 닫습니다. */
void close(int fd)
{
	if (fd < 0 || 64 <= fd)
		return;

간단한 보호 구문 추가

pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/close-normal:close-normal -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run close-normal < /dev/null 2> tests/userprog/close-normal.errors > tests/userprog/close-normal.output
perl -I../.. ../../tests/userprog/close-normal.ck tests/userprog/close-normal tests/userprog/close-normal.result
pass tests/userprog/close-normal
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/close-twice:close-twice -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run close-twice < /dev/null 2> tests/userprog/close-twice.errors > tests/userprog/close-twice.output
perl -I../.. ../../tests/userprog/close-twice.ck tests/userprog/close-twice tests/userprog/close-twice.result
pass tests/userprog/close-twice
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/close-bad-fd:close-bad-fd -- -q   -f run close-bad-fd < /dev/null 2> tests/userprog/close-bad-fd.errors > tests/userprog/close-bad-fd.output
perl -I../.. ../../tests/userprog/close-bad-fd.ck tests/userprog/close-bad-fd tests/userprog/close-bad-fd.result
pass tests/userprog/close-bad-fd

close 3종 전부 통과

file descriptor table

닫기 전에 모든 파일 닫기

void thread_exit(void)
{
	ASSERT(!intr_context());

#ifdef USERPROG
	// 모든 파일 close 해줘야됨
	struct file **fdt = thread_current()->fdt;
	for (int i = 2; i < 64; i++)
	{
		if (fdt[i] != NULL)
		{
			file_close(fdt[i]);
		}
	}

	free(thread_current()->fdt);
	process_exit();
#endif

저번에 안해줬던거 해줌.
터지진 않는데, 이거 한다고 뭐가 달라지진 않음.
그냥 requirement라서 해준거.

read

Reads size bytes from the file open as fd into buffer. 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 using input_getc().

깃북 번역본에 fd 0일땐 input_getc() 써야된다는 거 빠져있었다.

https://nullbyte.tistory.com/49

여기 보고서야 다시 확인함.

그와 별개로

/* Reads SIZE bytes from FILE into BUFFER,
 * starting at the file's current position.
 * Returns the number of bytes actually read,
 * which may be less than SIZE if end of file is reached.
 * Advances FILE's position by the number of bytes read. */
off_t file_read(struct file *file, void *buffer, off_t size)
{
	off_t bytes_read = inode_read_at(file->inode, buffer, size, file->pos);
	file->pos += bytes_read;
	return bytes_read;
}

read 자체는 file_read 쓰면 끝인듯.

/* fd로 열린 파일에서 size 바이트만큼 읽어서 buffer에 넣는다 */
int read(int fd, void *buffer, unsigned length)
{
	// fd에서 file 꺼내와서 file_read에 넣어준다
	struct file *file;
	file = thread_current()->fdt[fd];

	file_read(file, buffer, length);
}

일단 대충 만들어봤는데

pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/read-normal:read-normal -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run read-normal < /dev/null 2> tests/userprog/read-normal.errors > tests/userprog/read-normal.output
perl -I../.. ../../tests/userprog/read-normal.ck tests/userprog/read-normal tests/userprog/read-normal.result
FAIL tests/userprog/read-normal
Run started 'read-normal' but it never finished


pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/read-bad-ptr:read-bad-ptr -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run read-bad-ptr < /dev/null 2> tests/userprog/read-bad-ptr.errors > tests/userprog/read-bad-ptr.output
perl -I../.. ../../tests/userprog/read-bad-ptr.ck tests/userprog/read-bad-ptr tests/userprog/read-bad-ptr.result
FAIL tests/userprog/read-bad-ptr
Kernel panic in run: PANIC at ../../userprog/exception.c:97 in kill(): Kernel bug - unexpected interrupt in kernel
Call stack: 0x800421847f 0x800421cd91 0x800421cf10 0x8004208fbf 0x80042093dd 0x800421fb59 0x800421eade 0x800421d522 0x800421d185 0x800421cf85 0x400160 0x4001d6 0x400c77
Translation of call stack:
0x000000800421847f: debug_panic (lib/kernel/debug.c:32)
0x000000800421cd91: kill (userprog/exception.c:103)
0x000000800421cf10: page_fault (userprog/exception.c:159 (discriminator 12))
0x0000008004208fbf: intr_handler (threads/interrupt.c:352)
0x00000080042093dd: intr_entry (threads/intr-stubs.o:?)
0x000000800421fb59: inode_read_at (filesys/inode.c:219)
0x000000800421eade: file_read (filesys/file.c:86)
0x000000800421d522: read (userprog/syscall.c:240)
0x000000800421d185: syscall_handler (userprog/syscall.c:120)
0x000000800421cf85: no_sti (userprog/syscall-entry.o:?)
0x0000000000400160: (unknown)
0x00000000004001d6: (unknown)
0x0000000000400c77: (unknown)


pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/read-boundary:read-boundary -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run read-boundary < /dev/null 2> tests/userprog/read-boundary.errors > tests/userprog/read-boundary.output
perl -I../.. ../../tests/userprog/read-boundary.ck tests/userprog/read-boundary tests/userprog/read-boundary.result
pass tests/userprog/read-boundary


pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/read-zero:read-zero -p ../../tests/userprog/sample.txt:sample.txt -- -q   -f run read-zero < /dev/null 2> tests/userprog/read-zero.errors > tests/userprog/read-zero.output
perl -I../.. ../../tests/userprog/read-zero.ck tests/userprog/read-zero tests/userprog/read-zero.result
pass tests/userprog/read-zero


pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/read-stdout:read-stdout -- -q   -f run read-stdout < /dev/null 2> tests/userprog/read-stdout.errors > tests/userprog/read-stdout.output
perl -I../.. ../../tests/userprog/read-stdout.ck tests/userprog/read-stdout tests/userprog/read-stdout.result
FAIL tests/userprog/read-stdout
Kernel panic in run: PANIC at ../../userprog/exception.c:97 in kill(): Kernel bug - unexpected interrupt in kernel
Call stack: 0x800421847f 0x800421cd91 0x800421cf10 0x8004208fbf 0x80042093dd 0x800421d522 0x800421d185 0x800421cf85 0x40010d 0x40016b 0x400c0c
Translation of call stack:
0x000000800421847f: debug_panic (lib/kernel/debug.c:32)
0x000000800421cd91: kill (userprog/exception.c:103)
0x000000800421cf10: page_fault (userprog/exception.c:159 (discriminator 12))
0x0000008004208fbf: intr_handler (threads/interrupt.c:352)
0x00000080042093dd: intr_entry (threads/intr-stubs.o:?)
0x000000800421d522: read (userprog/syscall.c:240)
0x000000800421d185: syscall_handler (userprog/syscall.c:120)
0x000000800421cf85: no_sti (userprog/syscall-entry.o:?)
0x000000000040010d: (unknown)
0x000000000040016b: (unknown)
0x0000000000400c0c: (unknown)


pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/read-bad-fd:read-bad-fd -- -q   -f run read-bad-fd < /dev/null 2> tests/userprog/read-bad-fd.errors > tests/userprog/read-bad-fd.output
perl -I../.. ../../tests/userprog/read-bad-fd.ck tests/userprog/read-bad-fd tests/userprog/read-bad-fd.result
FAIL tests/userprog/read-bad-fd
Kernel panic in run: PANIC at ../../userprog/exception.c:97 in kill(): Kernel bug - unexpected interrupt in kernel
Call stack: 0x800421847f 0x800421cd91 0x800421cf10 0x8004208fbf 0x80042093dd 0x800421d185 0x800421cf85 0x40010d 0x400219 0x400cba
Translation of call stack:
0x000000800421847f: debug_panic (lib/kernel/debug.c:32)
0x000000800421cd91: kill (userprog/exception.c:103)
0x000000800421cf10: page_fault (userprog/exception.c:159 (discriminator 12))
0x0000008004208fbf: intr_handler (threads/interrupt.c:352)
0x00000080042093dd: intr_entry (threads/intr-stubs.o:?)
0x000000800421d185: syscall_handler (userprog/syscall.c:120)
0x000000800421cf85: no_sti (userprog/syscall-entry.o:?)
0x000000000040010d: (unknown)
0x0000000000400219: (unknown)
0x0000000000400cba: (unknown)

살짝 난해한 결과가 나왔다.

read-normal: 끝나지를 않는다고 함. 확인해보니 다른 테케들은 read를 직접 호출하는데 이것만 check_file을 호출함. 그 안에서 read가 호출되는 느낌.
read-boundary, read-zero: 이건 또 왜 통과함? normal은 안되면서?
read-bad-ptr, read-stdout, read-bad-fd: 페이지 폴트 나면서 터짐. 이건 보호구문만 처리하면 되겠지?

read-bad-fd

int read(int fd, void *buffer, unsigned length)
{
	if (fd < 0 || 64 <= fd || !is_user_vaddr(buffer))
		exit(-1);

	// fd에서 file 꺼내와서 file_read에 넣어준다
	struct file *file;
	file = thread_current()->fdt[fd];

	return file_read(file, buffer, length);
}

bad-fd는 통과

read-bad-ptr

/* fd로 열린 파일에서 size 바이트만큼 읽어서 buffer에 넣는다 */
int read(int fd, void *buffer, unsigned length)
{
	if (fd < 0 || 64 <= fd || pml4_get_page(thread_current()->pml4, buffer) == NULL || !is_user_vaddr(buffer))
		exit(-1);

	// fd에서 file 꺼내와서 file_read에 넣어준다
	struct file *file;
	file = thread_current()->fdt[fd];

	return file_read(file, buffer, length);
}

앞에서 했던 것처럼
page 유효성 검사도 해줘야 함

그랬더니 통과

read-stdout

/* Try reading from fd 1 (stdout), 
   which may just fail or terminate the process with -1 exit
   code. */

어떻게 해야되지? stdout이면 1인데 왜 깃북에선 stdin의 fd인 0을 얘기하고 있는거지? 이러다가
테스트 파일 보니까 주석에 "이거 터트려야돼요" 라고 함.

	if (fd < 0 || 64 <= fd || fd == 1 || pml4_get_page(thread_current()->pml4, buffer) == NULL || !is_user_vaddr(buffer))
		exit(-1);

fd==1 조건 추가해서 통과

read-normal

normal은 lib.c에 있는 check_file()을 사용함. 디버깅으로 문제 좁혀봤더니 여기가 문제.

ctrl 좌클릭해도 안 들어가지길래 검색해봤더니 내가 그냥 구현을 안해서 그런거였음

/* fd(첫 번째 인자)로서 열려 있는 파일의 크기가 몇 바이트인지 반환합니다. */
int filesize(int fd)
{
	struct file *file = thread_current()->fdt[fd];

	return file_length(file);
}

구현했더니 통과

write

/* buffer로부터 open file fd로 size 바이트를 적어줍니다. */
int write(int fd, const void *buffer, unsigned length)
{
	if (fd < 0 || 64 <= fd || fd == 0 || pml4_get_page(thread_current()->pml4, buffer) == NULL || !is_user_vaddr(buffer))
		exit(-1);

	// strlcpy(fd, buffer, length);
	if (fd == 1)
	{
		putbuf(buffer, length);
		return length;
	}

	struct file *file = thread_current()->fdt[fd];

	return file_write_at(file, buffer, length, 0);
}

가볍게 다 통과

fork

OSTEP

자식 프로세스는 fork()를 호출한 부분에서부터 시작

fprintf : 파일에 데이터 쓰기

자식 프로세스는 자신의 주소 공간, 레지스터, PC 값을 가짐

프로그램 카운터(Program counter, PC) : 프로세서 내부에 있는 레지스터 중의 하나, 다음에 실행될 명령어의 주소를 가지고 있음

fork로부터 부모는 자식의 PID 반환받고, 자식은 0 반환 받음.

단일 cpu 시스템에선 부모 자식 둘 중 하나 실행됨

exec()는 현재 실행 중 프로세스의 코드 세그멘트와 정적 데이터 부분을 덮어 씀. 힙과 스택들도 초기화됨. 그런 다음 argv 같은 인자를 전달해서 프로그램 실행시킴.

__do_fork

static void
__do_fork(void *aux)
{
	struct intr_frame if_;
	struct thread *parent = (struct thread *)aux;
	struct thread *current = thread_current();
	/* TODO: somehow pass the parent_if. (i.e. process_fork()'s if_) */
	struct intr_frame *parent_if = &parent->user_tf;
	bool succ = true;

	/* 1. Read the cpu context to local stack. */
	memcpy(&if_, parent_if, sizeof(struct intr_frame));

	/* 2. Duplicate PT */
	current->pml4 = pml4_create();
	if (current->pml4 == NULL)
		goto error;

	process_activate(current);
#ifdef VM
	supplemental_page_table_init(&current->spt);
	if (!supplemental_page_table_copy(&current->spt, &parent->spt))
		goto error;
#else
	if (!pml4_for_each(parent->pml4, duplicate_pte, parent))
		goto error;
#endif

	/* TODO: Your code goes here.
	 * TODO: Hint) To duplicate the file object, use `file_duplicate`
	 * TODO:       in include/filesys/file.h. Note that parent should not return
	 * TODO:       from the fork() until this function successfully duplicates
	 * TODO:       the resources of parent.*/

	// 파일 식별자 복사
	for (int i = 2; i < 64; i++)
		current->fdt[i] = file_duplicate(parent->fdt[i]);

	process_init();

	// 부모 리스트에 자식 추가
	list_push_back(&parent->child_list, &current->child_elem);

	/* Finally, switch to the newly created process. */
	if (succ)
		do_iret(&if_);
error:
	thread_exit();
}

duplicate_pte

static bool
duplicate_pte(uint64_t *pte, void *va, void *aux)
{
	struct thread *current = thread_current();
	struct thread *parent = (struct thread *)aux;
	void *parent_page;
	void *newpage;
	bool writable = false;

	/* 1. TODO: If the parent_page is kernel page, then return immediately. */
	if (is_kern_pte(pte))
		return false;

	/* 2. Resolve VA from the parent's page map level 4. */
	parent_page = pml4_get_page(parent->pml4, va);

	/* 3. TODO: Allocate new PAL_USER page for the child and set result to
	 *    TODO: NEWPAGE. */
	newpage = palloc_get_page(PAL_USER);

	/* 4. TODO: Duplicate parent's page to the new page and
	 *    TODO: check whether parent's page is writable or not (set WRITABLE
	 *    TODO: according to the result). */
	if ((*pte & PTE_W) == PTE_W)
		writable = true;

	if (writable)
		memcpy(newpage, parent_page, PGSIZE);

	/* 5. Add new page to child's page table at address VA with WRITABLE
	 *    permission. */
	if (!pml4_set_page(current->pml4, va, newpage, writable))
	{
		/* 6. TODO: if fail to insert page, do error handling. */
		pml4_destroy(current->pml4);
		return false;
	}
	return true;
}

일단 하긴 했는데 wait 구현 전엔 통과 못한다고 함.

0개의 댓글