/////
Search

Project2: User Programs

이제 핀토스 좀 해봤으니, 이제 전체적인 infrastructure랑 thread package에 친해졌겠지?
그럼 이제 user program을 돌릴 수 있는 파트를 해보자. 기본 코드는 이미 유저 프로그램의 실행과 로딩을 지원하지만, I/O와 interactivity는 가능하지 않아. 이번 프로젝트에서, 너는 OS와 시스템콜로 소통할 수 있게끔 할거야
너는 userprog라는 디렉토리에서 작업하게 될건데, 사실 핀토스의 거의 모든 파트를 들여다보게 될거야. 우리는 관련있는 파트들을 아래에서 소개해줄게.
그리고 프로젝트 2는 너가 만든 프로젝트1에서 다시 시작할 수도 있고, fresh하게 새로 시작할 수도 있다. 이번 과제에서는 프로젝트 1에서의 코드는 필요 없어. “alarm clock” 기능은 프로젝트 3,4에서 유용하겠지만, 엄격하게 얘기해서 필요하진 않아.
그리고 테스트를 어떻게 실행시키는지 다시 가서 읽어보는 것을 추천해

Background

지금까지는, 니가 짠 모든 코드들, 그니까 핀토스에서 돌아간 너의 코드는 커널 운영체제의 일부분이었어. 이 말인즉슨, 모든 테스트코드들은 커널의 일부분으로 돌아갔다는 거고, 시스템에서 privileged된 부분에 모든 권한이 있었다는 뜻이야. 근데 이제 user program을 운영체제 위에서 돌리기 시작하면, 더 이상 그럴 수 없다. 이번 프로젝트는 이런 결론 위에서 다뤄질거야.
우리는 한 번에 한 프로세스 이상을 돌리는 것을 허용할거야. 각 프로세스는 하나의 쓰레드를 가지고 있어.(멀티 쓰레드는 지원을 못해...) 유저 프로그램은 그들이 전체 머신을 차지하고 있다고 생각하고 짜여질거야. 이 말은 너가 여러 프로세스를 한 번에 로드하고 실행시킬거고, 메모리도 관리하고, 스케쥴링도 해야하고 다른 상태들도 이런 illusion에 맞게끔 정확하게 유지해야한다는 뜻이지.
이전 프로젝트에서 우리는 테스트코드를 직접적으로 너의 코드와 함께 컴파일을 시켰고, 그래서 우리는 몇 가지 특정 함수 인터페이스들을 커널 안에서 필요로 했어. 이제부터는 우리는 너의 운영체제를 사용자 프로그램을 돌리면서 테스트할거야. 이건 좀 더 많은 자유를 줄거야. 이제는 사용자 프로그램 인터페이스가 가져야할 스펙들을 보게 될텐데, 이런 스펙들 안에서 커널 코드를 새로 쓰던, 재구성하든 맘대로 해도 된다.

Source Files

너가 짤 프로그램을 일단 전체적으로 보자. userprog 에서, 너는 몇 가지 파일을 볼 수 있을건데, 아래에 있는게 그 묶음이야.
process.c
process.h
Loads ELF binaries and starts process
pagedir.c
pagedir.h
x86 하드웨어 페이지 테이블을 위한 간단한 manager다. 너가 여기 있는 코드들을 수정하고 싶지 않더라도, 너는 여기 있는 함수들을 호출하게 될거야. 4.1.2.3의 페이지테이블 파트를 보렴.
syscall.c
syscall.h
사용자 프로세스가 커널의 기능에 접근하고 싶어할 때마다, 이건 시스템 콜을 호출할거야. 이건 시스템 콜 핸들러를 위한 스켈레톤 코드이다. 지금은 그냥 메시지만 출력하고 프로세스를 종료시켜. 프로젝트2에서 시스템 콜 호출을 위한 다른 모든 것들을 여기다가 써야해
exception.c
exception.h
만약 사용자 프로세스가 privileged나 금지된 연산을 시행한다면, 이건 kernel에 “exception”이나 “fault”의 형태로 trap을 보낸다. 이 파일들은 exceptioin을 담당한다. 현재는 모든 예외가 그냥 메시지를 출력하고 프로세스를 종료하는 식으로 짜여져있어. 프로젝트2의 솔루션들 중에, 전체는 아니고 일부분이 page_fault()를 수정하기를 요구할거야
gdt.c
gdt.h
x86은 segmented architecture이다. Global Descriptor Table은 현재 사용되고 있는 세그먼츠를 설명하는 테이블이다. 이 파일은 GDT를 셋업할 거야. 그리고 너는 이 파일들을 다른 프로젝츠에서는 수정하면 안돼. 만약에 GDT가 어떻게 돌아가는지 궁금하다면 한 번 읽어봐
tss.c
tss.h
Task-State Segment는 x86 아키텍처의 task switching을 위해 사용되. 핀토스는 TSS를 유저 프로세스가 인터펍트 핸들러를 만났을 때, 스택을 변경하는 것만을 위해 써. 너는 이 파일을 다른 프로젝트에서 수정하면 안돼. TSS가 어떻게 돌아가는지 알고 싶으면 읽어봐

파일 시스템 사용하기

너는 이번 프로젝트에서 파일 시스템 코드의 인터페이스가 필요할거야. 왜냐하면 유저 프로그램이 파일 시스템에서 로드가 될거고 너가 구현할 많은 시스템 콜들이 파일 시스템을 다루고 있기 대문이지. 그렇지만 이번 프로젝트는 파일 시스템에 초점을 맞추는게 아니기에, 우리는 간단하지만 완벽한 파일 시스템을 ‘filesys’디렉토리에 만들어놨어. 만약에 어떻게 쓰는지 알고 싶으면 ‘file.h’나 ‘filesys.h’를 한 번 봐바. 보면은 제약도 많을 거야.
이 파일 시스템 코드를 고칠 필요도 없고 그러지 말기를 바라. 파일 시스템을 건드리는 것은 프로젝트의 초점을 흐리게 만들거야.
파일 시스템 루틴을 적절히 사용하는 것을 프로젝트4를 하는데 좀 더 편하게 해줄 거야. 프로젝트4에서 파일 시스템 구현을 improve할 꺼거든. 그 때까지는 다음과 같은 limitation을 용인해줘야해.
No internal synchronization
하나에 대해서 동시에 접근할 수 없어. 파일 시스템 코드를 실행하는데 있어서 오직 하나의 프로세스만 돌아가도록 무조건 synchronziation을 사용해야해
파일 사이즈가 creation time에 무조건 정해져. root 디렉토리가 파일로 나타나기에, 만들어질 수 있는 파일의 숫자 또한 제한적이야.
디스크의 연속적인 섹터를 무조건 차지하게 될거야. 그니까 하나의 파일 내의 데이터들은 무조건 디스크에서 연속적인 섹터를 차지해. External fragmentation은 나중에 가서 파일 시스템에서 serious problem이 될거야.
Subdirectory 따윈 없어
파일 이름은 14글자로 제한돼
중간 연산을 하다가 뻑나는거는 디스크를 망쳐버릴 수 있고, 이건 자동으로 복구가 안돼. 파일 시스템 repair 툴이 없어
한 가지 더 중요한게 남아있는데
filesys_remove()를 위한 유닉스 스타일의 semantic이 구현되어있어. 이 말은 파일이 열려있다가 지워진다면, 그 블록들은 할당 해제가 되지 않고, 그 구역은 이미 그 구역을 열었던 쓰레드에 의해, 마지막 쓰레드가 닫힐 때까지 접근이 가능해. Removing an Open File, 35페이지를 봐.
파일 시스템 파티션이 딸린 simulated disk를 만들 수 있어야겠지?, pintos-mkdisk 프로그램은 이 기능을 제공해줘. ‘userprog/build’ 디렉토리에서, pintos-mkdisk filesys.disk —fiilesys-size=2를 실행해봐. 이 커맨드는 filesys.dsk라는 이름을 가진 simulated disk를 만들어주고, 2MB 짜리 핀토스 파일 시스템 파티션을 만들어낼꺼야. 그리고 파일 시스템 파티션을 ‘-f -q’ 옵션으로 줘서 포맷시킬 수 있을거야(pintos -f -q). ‘-f’ 옵션은 파일 시스템을 포맷 시켜주고, ‘-q’는 핀토스로 하여금 포맷을 빨리 끝내도록 할거야.
너는 또한 파일 시스템 상에서 파일들을 복사할 방법이 필요할 거야. pintos의 ‘-p’와 ‘-g’ 옵션은 이 행동을 할 거야. 파일을 핀토스 파일 시스템에 복사하기 위해서는 ‘pintos -p file — -q’ 옵션을 써. ( ‘—’는 ‘-p’가 실행중인 커널을 위한 게 아니라, 핀토스를 나타내기 위해 필요해). 새로운 이름으로 파일을 복사하고 싶다면 ‘-a newname’을 붙여봐. 그럼 ‘pintos -p file -a newname — -q’가 되겠지? VM 없이 파일을 복사하는 커맨드는 ‘-p’ 대신에 ‘-g’를 쓸거야.
우연히, 이런 명령들은 특별한 명령인 extract이나 append 같은 커널의 커맨드 라인을 패싱함으로써, 그리고 특별히 시뮬레이션되는 “scratch” 파티션에서 실행될거야(뭔소리야.... Incidentally, these commands work by passing special commands extract and append on the kernel’s command line and copying to and from a special simulated “scratch” partition.)
정말 궁금하다면 filesys/fsutil.c에 있는 핀토스 스크립트를 한 번 봐.
아래는 파일 시스템 파티션과 함께 디스크를 만들고, 포맷하고, echo program을 새로운 디스크로 옮겨서 어떻게 run을 하는지 보여줘. 근데 argument passing은 너가 구현하기 전까지는 작동 안되겠지? 여기서는 ‘examples’ 안에 있는 example을 이미 빌드를 했고, 현재 디렉토리는 ‘userprog/build’라고 가정한다.
pintos-mkdisk filesys.disk --filesys-size=2 pintos -f -q pintos -p ../../examples/echo -a echo -- -q pintos -q run 'echo x'
C
복사
너가 만약 파일 시스템 디스크를 나중에 사용하기 위해서, 또는 inspection 하기 위해서, 별로 남겨 놓고 싶지 않다면, 너는 이 네 가지 과정을 한 줄로 줄여서 할 수 있어 —filesys-size=n 옵션은 거의 n 메가 바이트 사이즈의 임시 파일 시스템 파티션을 핀토스가 진행 중일 동안 만들어. 핀토스의 자동 테스트는 아래와 같은 문법을 되게 많이 쓰겠지?
pintos --filesys-size=2 -p ../../examples/echo -a echo -- -f -q run 'echo x'
C
복사
너는 그리고 rm file을 이용해서 핀토스 파일 시스템에서 파일을 지울 수 있어. 예를 들어 pintos -q rm file 이렇게. 뿐만 아니라 ls는 파일 시스템의 파일들을 리스트 해주고 cat은 파일들 내용물을 디스플레이에 띄워줘

유저 프로그램은 어떻게 돌아가냐

핀토스는 메모리에 딱 맞는 애들이랑 너가 구현한 시스템 콜만 사용하는 C 프로그램들을 돌릴 수 있어. malloc()은 구현할 수 없는데, 왜냐하면 이 프로젝트에서 메모리 할당을 허용하는 시스템 콜이 필요한 경우가 없기 때문이이ㅑ. 핀토스는 또 부동 소수점 연산을 사용하는 애들은 못 돌려, 왜냐하면 커널이 프로세서의 부동 소수점을 저장하고 회복시키지 못하기 때문이야.
‘src/examples’의 디렉토리는 사용자 프로그램을 위한 몇 가지 샘플을 담고 있어. 이 디렉토리에 있는 Makefile은 제공된 예시들을 컴파일 시키고, 그 프로그램을 니 입맛대로 고칠 수 있어. 몇 가지 예제 프로그램들은 프로젝트3나 4가 구현해줄 때에만 돌아가는 경우도 있어.
핀토스는 ELF 실행파일을 userprog/process.c에 제공된 로더를 이용해서 실행할 수 있어. ELF는 linux, solaris 그리고 많은 다른 운영체제에서 사용되는 파일 포맷이야. 너는 실제로 x86 ELF 실행파일을 만들어내는 컴파일러나 링커를 사용하면 핀토스를 위한 프로그램을 만들어낼 수 있어.(우리는 이를 위한 컴파일러랑 링커를 줄거니까 너는 충분히 할 수 있을 거야)
너는 그리고 즉시 이걸 개닫겠지만, 너가 시뮬레이션된 파일 시스템으로 테스트 프로그램을 들고올 때까지, 핀토스는 userful한 작업을 할 수 없어. 그러니까 파일 시스템 안으로 다양한 종류의 프로그램을 복사해오기 전까지는 뭘 못할거야. 너는 가금 새로운 파일 시스템 디스크 레퍼런스를 만들고 싶을 때가 있을 거야. 가끔 디버깅을 위한 걸테지만, 파일 시스템이 완전 깨끗한 상태더라도 버리게 될 거다.

Virtual Memory Layout

핀토스 내의 가상 메모리는 두 부분으로 나뉘어져 있다. 사용자 가상 메모리와 커널 가상 메모리이다. 사용자 가상 메모리는 0부터 PHYS_BASE까지 범위를 가지는데, 기본적으로는 3GB를 가지고 threads/vaddr.h에 정의되어있어. 커널 가상 메모리는 나머지 가상 메모리를 가지는데, 결국 3GB부터 4GB까지를 가지겠지?
사용자 가상 메모리는 프로세스마다 있는거야. 만약 커널이 한 프로세스에서 다른 걸로 옮겨가면, 이건 프로세서의 페이지 디렉토리 base register를 바꿈으로써 사용자 가상 메모리 또한 바꿀거다. (pagedir_activate()를 봐라). struct thread는 프로세스당 페이지 테이블의 포인터를 가리킨다.
커널 가상 메모리는 전역이야. 이건 항상 같은 방식으로 매핑된다. 어떤 유저 프로세스나 커널 쓰레드가 돌고 있는지랑 상관없이. 핀토스에서 커널 가상 메모리는 1:1로 PHYS_BASE에 시작하는 물리 메모리랑 매핑이 되. 이 말은 가상 주소 공간 PHYS_BASE에 대한 접근이 물리 주소 공간 0이 된다는 거고, 가상 주소공간으로 PHYS_BASE + 0x1234는 물리 주소 공간으로 0x1234에 접근하는거랑 같은거야. 따라서 전체 머신의 물리 주소의 사이즈랑 거의 똑같지(?)
사용자 프로그램은 오직 자신의 가상 메모리에만 접근할 수 있어. 커널 가상 메모리에 접근하면 page_fault가 날꺼고, page_fault()함수에 의해 핸들링이 되고 프로세스는 종료될거야. 커널 쓰레드는 커널 가상 메모리나 사용자 메모리에도 접근할 수 있어. 하지만 커널도 마찬가지로, 사상되지 않은 사용자 가상 메모리에 접근하려면 page fault를 일으켜.

Typical Memory Layout

비슷하게, 각 프로세스는 각자의 사용자 가상 주소를 설계하는 건 자유야. 실제로, 사용자 가상 메모리는 다음처럼 매핑이 될거다.
이 프로젝트에서, 사용자 스택은 사이즈가 고정되어있지만, 프로젝트 3에서는 자라는게 허용된다. 전통적으로 초기화되지 않은 데이터 구역은 시스템 영역으로 조정될 수 있지만, 여기서는 그거까지는 안해도 돼.
핀토스에서 코드 구역은 사용자 가상 주소 상 0x08084000으로 시작하고, 거의 아래에서 128MB 지점에서 시작하는거야. 이 값은 [SysV-i386]에 정의가 되어있고, 뭐 큰 중요성은 없다.
링커는 메모리에 사용자 프로그램의 레이아웃을 설정하고, 이건 다양한 프로그램 세그먼츠의 위치와 이름을 말해주는 ‘linker script’에 의해 지시되. 너는 링커 스크립트에 대해서는 링커 메뉴얼에 나와있는 “Scripts” 챕터에서 자세히 읽을 수 있고, info ld로 접근할 수 있어
특정 실행 파일의 레이아웃을 보기 위해 objdump 또는 i386-elf-objdump를 -p 옵션과 함께 실행시켜봐. (???)

사용자 메모리 접근하기

시스템 콜 파트에서, 커널은 사용자 프로그램이 제공한 포인터를 통해 메모리에 접근해야하는 경우가 있어. 커널은 이런 짓을 하는거에 매우 신중해. 왜냐면 사용자가 널 포인터를 넘겨줄 수도 있고, 아직 사상되지 않은 포인터를 넘겨줄 수도 있고, 커널의 가상 주소에 대한 포인터를 넘겨줄 수도 있기 때문이지. 이런 모든 Invalid 포인터에 대한 것들은 커널이나 다른 실행 중인 프로세스에 위해가 되지 않도록 리젝되야하고, 이런 짓을 하는 프로세스를 종료시키고 자원을 해제시켜야해.
이를 위해 2가지 정도의 방법이 있어. 첫번재는 사용자가 제공한 포인터를 적절히 validate하는 거고. 다른 하는 userprog/pagedir.c랑 threads/vaddr.h에 있는 함수들을 사용할 수도 있어. 이 방법이 아마 사용자 메모리 접근을 제어하는 가장 쉬운 방법이겠지?
두 번째 방법은 사용자 포인터가 PHYS_BASE 아래에 있는 포인터를 가리키는지를 확인해서 역참조시켜주는게 다야. 만약 부적절한 사용자 포인터가 page fault를 일으키면, 너가 구현한 page_fault() 함수로 적절히 핸들할 수 있어야해. 이런 테크닉은 보통 빠른데, 왜냐면 프로세서의 MMU의 이점을 가지기 때문이야. 그리고 이건 주로 실제 커널에서 사용하는 방법임.
다른 경우에 대해서, 너는 리소스의 leak이 일어나지 않도록 해야해. 예를 들어서, 시스템 콜이 락을 획득하거나 malloc을 이요해서 메모리를 얻었다해보자. 만약 너가 부적절한 사용자 포인터를 만났더라도, lock을 적절히 해제하거나 메모리를 적절히 해제하는게 필수적이야. 역참조하기 전에 verify하는게 좋겠지? 만약 부적절한 포인터가 page fault를 일으켰다면 더 다루기 어려운데, 왜냐면 메모리 접근으로부터 에러 코드를 리턴 받을 방법이 없기 때문이야. 그렇기에 후자의 테크닉을 시도해보고 싶은 애들 위해, 약간의 도움되는 코드를 줄게.
/* Reads a byte at user virtual address UADDR. UADDR must be below PHYS_BASE. Returns the byte value if successful, -1 if a segfault occurred. */ static int get_user (const uint8_t *uaddr) { int result; asm ("movl $1f, %0; movzbl %1, %0; 1:" : "=&a" (result) : "m" (*uaddr)); return result; } /* Writes BYTE to user address UDST. UDST must be below PHYS_BASE. Returns true if successful, false if a segfault occurred. */ static bool put_user (uint8_t *udst, uint8_t byte) { int error_code; asm ("movl $1f, %0; movb %b2, %1; 1:" : "=&a" (error_code), "=m" (*udst) : "q" (byte)); return error_code != -1; }
C
복사
각 함수는 모든 사용자 주소가 이미 PHYS_BASE 아래에 있다고 가정해. 뿐만 아니라 저건 너가 이미 page_fault()를 수정해서, page fault가 커널로 하여금 eax를 0xffffffff로 설정하게 하고, 이전 값을 eip로 옮겨주게끔 할거야.

Suggested Order of Implementation

우리는 구현을 아래와 같은 순서로 하는 걸 제안할게
Argument PAssing
모든 사용자 프로그램은 argument passing이 구현되기 전까지 페이지 폴트가 날거야.
지금부터 너는 *esp = PHYS_BASE를 *esp = PHYS_BASE -12로 바꾸게 될 거다(setup_stack())
프로그램의 이름이 null로 프린트가 되더라도, 어떤 테스트 프로그램도 argument를 평가하지 않게 될거야. 너가 arugment passing을 구현하기 전까지, 너는 command-line argument가 없는 프로그램만 돌릴 수 있어. argument를 패싱하는 것을 시도한다면, 그리고 그 argument에 프로그램 일므도 있을 텐데, 아마 실패할걸?
User memory Access
모든 시스템 콜은 사용자 메모리를 읽어야해. 몇몇 시스템 콜은 사용자 메모리에 쓰기를 필요로 해.
시스템 콜 infrastructure.
사용자 스택에서 시스템 콜 숫자를 익어들여서 적절한 핸들러에 dispatch 시키는 것을 구현해야한다.
exit system call
모든 사용자 프로그램은 정상적으로 끝난다면 exit을 호출해. 메인 함수에서 return을 하더라도 exit을 암묵적으로 호출한다.
write system call
fd 1에 writing을 하는 걸 짜자. 이건 system console로 결국 출력하는거다. 모든 테스트 프로그램은 콘솔로 쓰기를 한다,(printf()가 이 방식으로 구현됨) 그래서 이거 구현하기 전까지는 제대로 안 돌아갈거야
process_wait()를 무한루프로 바꾸자
제공된 구현은 즉시 return한다. 그래서 핀토스는 어떤 프로세스가 시작하기도 전에 핀토스가 종료해버린다. 너는 최종적으로 올바른 구현을 제공해야한다.
위에 설명한걸 구현한다면, 사용자 프로세스는 minimally 작동할거다. 최소한 그들은 콘솔에다가 잘 쓰고 잘 나갈거다. 이 구현을 적당히 개선하면 테스트들이 패스하기 시작할거야.

참고사항들

명령어 실행 기능 구현

process_execute()
file_name 문자열을 파싱
첫 번재 토큰을 thread_create()함수에 스레드 이름으로 전달.
지금은 들어오는 string을 그대로 쓰레드 이름으로 박아넣는데, 한 번 스페이스를 기준으로 잘라서 넣자. 그리고 나머지 애들을 argument로 삽입.
start_process()
file_name 문자열 파싱
argument_stack()함수를 이용해 스택에 토큰들을 저장
흠.... load랑 setup_stack 쪽을 다시 한번 봐야할거 같은데...
argument_stack(char ** parse, int count, void **exp)
parse: 프로그램 이름과 인자가 저장되어있는 메모리 공간
count: 인자의 개수
esp: 스택 포인터를 가리키는 주소
유저 스택에 프로그램 이름과 인자들을 저장하는 함수

의문점

왜 file_name을 복사를 해서 fn_copy를 만들지??
race가 있다고 합니다....
load의 역할을 무엇인가
ELF 헤더를 읽어들이는 역할 by load_segment. 그리고 헤더를 표현하는 구조체에 저장함.
stack을 할당하는 역할 by setup_stack
eip를 해당하는 읽어들인 ELF파일의 시작점에 갖다놓는 역할
setup_stack의 역할을 무엇인가
esp라는 스택포인터를 설정
page_table에다가 엔트리를 추가해야 프로세스를 실행할 때 읽어들이겠지?
그래서 빈 페이지 하나를 할당하고, 사용자 프로세스의 가상주소랑 실제 물리 주소를 매핑
그리고 esp를 해당하는 사용자 가상 주소에다가 맞춰놓음
argument_stack의 구현
메모리 할당도 되있겠다. esp를 하나씩 조작해가면서 값을 채워넣으면 된다.

system call

현재 핀토스에서 시스템 콜 호출 과정
1.
유저 프로그램이 함수를 호출하면
2.
lib/user/syscall.c에 있는 함수가 불러짐
a.
이 파일에는 syscall이 유형별로 분류가 되있지만, 모두 동일한 interrupt vector number를 부름.
b.
따라서 항상 동일한 syscall_handler()라는 함수가 호출되게 된다.
c.
왜냐하면 맨 처음에 syscall_init이라는 함수에 의해서 syscall_handler가 0x30번 인터럽트에 대한 핸들러로 달라붙기 때문
d.
syscall 별로 분류가 되있는 이유는 결국 파라미터를 뭘 넣을 것이며, 어떤 타입으로 return을 받을건지에 따라 분류되있는 듯하다.
3.
syscall_handler함수는 시스템 콜 번호를 이용해서 시스템 콜을 호출한다.
a.
이 시스템 콜 번호는 src/lib/syscall_nr.h에 정의되어있음.

해야할 거

시스템 콜 핸들러 구현
시스템 콜 번호를 이용하여 해당 시스템 콜의 서비스 루틴을 호출 하도록
유저 스택 포인터(esp) 주소와 시스템 콜 인자가 가리키는 주소(포인터)가 유효 주소(유저 영역)인지 확인하도록
유저 스택에 존재하는 스택 프레임의 인자들을 커널에 복사하도록 구현(왜...?)
시스템 콜 함수의 리턴 값은 인터럽트 프레임의 eax에 저장되도록 구현
void check_address(void *addr)
주소 값이 유저 영역에서 사용하는 주소 값인지 확인 하는 함수
유저 영역을 벗어난 영역일 경우 프로세스 종료 exit(-1)
void get_argument(void *esp, int *arg, int count)
유저 스택에 있는 인자들을 커널에 저장하는 함수
스택 포인터(esp)에 count(인자의 개수) 만큼의 데이터를 arg에 저장
??? 이게 무슨 말이지
스택 포인터가 유저 영역인지 확인
스택에서 시스템 콜 넘버 복사
시스템 콜 넘버에 따른 인자 복사 및 시스템 콜 호출
시스템 콜 구현
API 제공해줌
shutdown_power_off(void)
핀토스를 종료시키는 함수
thread_exit
스레드를 종료시키는 함수
filesys_create
파일 이름과 파일 사이즈를 인자 값으로 받아 파일을 생성하는 함수
filesys_remove
파일 이름에 해당하는 파일을 제거하는 함수
void halt(void)
void exit(int status)
bool create(count char *file, unsigned initial_size)
파일을 생성하는 시스템 콜
성공일 경우 true, 실패 일 경우 false
file: 생성할 파일의 이름 및 경로 정보
initial_size: 생성할 파일의 크기
bool remove(const char *file)
파일을 삭제하는 시스템 콜
file: 제거할 파일의 이름 및 경로 정보
성공일 경우 true, 실패일 경우 false 리턴

의문점

사용자 모드에서 커널 모드로 바뀌는 건 언제하는거지...