* 시스템 호출의 구현
제출일: 2012. 11. 27.
제출물:
- 디자인문서: 시스템 호출을 어떻게 설계했는지 설명. 설계고려사항에 대한 답.
- 소스코드
- 보고서: 시험 결과
이 과제에서 구현해야 하는 시스템 호출은 다음과 같다.
1. 멀티프로그래밍 관련: getpid, fork, execv, waitpid, _exit
2. I/O : readchar, printchar
(참조: 전체 시스템 호출은 kern/include/kern/callno.h 에서 볼 수 있음.)
- 시스템 호출에서는 에러의 처리를 반드시 해야 하며, 오류 발생으로 OS161이 crash하지 않도록 해야 한다. man/syscall 의 문서를 읽어보기 바람.
- 시스템 호출은 반드시 적절한 값을 return value로 반환해야 한다. 에러가 날 경우 man page에 정의된 에러코드를 반환해야 한다. 구현이 정확한 지 확인하기 위해서 이 값이 사용되므로, 이를 반드시 구현해야 한다. 에러코드는 kern/errno.h 에 정의된다.
- 사용자 interface의 정의는 include/unistd.h 에서 볼 수 있으며, 이 인터페이스를 설계해서 kern/include/syscall.h 에 넣어야 한다.
- 구현에 있어서 kill_curthread()를 작성해야 할 필요가 있는데, 최대한 단순하게 구현해도 문제는 없다.
- 각 함수에 대한 설명
A pid, or process ID, is a unique number that identifies a process. The implementation of getpid() is not terribly challenging, but pid allocation and reclamation are the important concepts that you must implement. It is not OK for your system to crash because over the lifetime of its execution you've used up all the pids. Design your pid system; implement all the tasks associated with pid maintenance, and only then implement getpid().
int printchar(char i)
The printchar() system call enables user programs to output text to the screen. printchar() takes a single character and prints it to the screen. It returns zero on success and -1 if there is an error. Printchar() always prints to stdout.
The readchar() system call lets user programs read input from the keyboard. This system call should return a single character that the user types in the terminal to your program. Make sure that this system call returns end-of-file correctly. Also, you many need to handle end-of-line characters correctly. readchar() does not take any arguments and only reads from stdin.
For both readchar() and printchar() you will need to modify various libc routines to call this system call instead of the read() and write() system calls, which are not implemented. You don't need to support libc calls that use anything other than stdin or stdout.
fork() is the mechanism for creating new processes. It should make a copy of the invoking process and make sure that the parent and child processes each observe the correct return value (that is, 0 for the child and the newly created pid for the parent). You will want to think carefully through the design of fork() and consider it together with execv() to make sure that each system call is performing the correct functionality.
execv(), although "only" a system call, is really the heart of this assignment. It is responsible for taking newly created processes and making them execute a different program (i.e., a program different than what the parent is executing). Essentially, it must replace the existing address space with a brand new one for the new executable (created by calling as_create in the current dumbvm system) and then run it. While this is similar to starting a process straight out of the kernel (as runprogram() does), it's not quite that simple. Remember that this call is coming out of userspace, into the kernel, and then returning back to userspace. You must manage the memory that travels across these boundaries very carefully. (Also, notice that runprogram() doesn't take an argument vector. This must be handled correctly in execv() and also in runprogram()).
Although it may seem simple at first, waitpid() requires a fair bit of design. Read the specification carefully to understand the semantics, and consider these semantics from the ground up in your design. You may also wish to consult the UNIX man page, though keep in mind that you are not required to implement all the things UNIX waitpid() supports.
The implementation of _exit() is intimately connected to the implementation of waitpid(). They are essentially two halves of the same mechanism. Most of the time, the code for _exit() will be simple and the code for waitpid() relatively complicated -- but it's perfectly viable to design it the other way around as well. If you find both are becoming extremely complicated, it may be a sign that you should rethink your design.
- 설계 고려 사항
1. 사용자 프로그램을 동작 시킬 때, stack pointer 의 초기값은 어떻게 주는가?
2. execv 에서 프로그램의 argument를 어떻게 넘겨 주는가?
3. waitpid와 _exit을 구현함에 있어, thread_join 과 thread_detach 가 어떻게 사용되는가?
4. waitpid와 _exit에서 동기화 문제가 있는가? 있다면 해결책은 무엇인가?
보고서에 위 문제에 대한 답을 기재하라.
- 테스트
1. 먼저 /testbin에 있는 프로그램들을 수행하여 테스트할 수 있다. 테스트에 사용할 수 있는 프로그램은 다음과 같다.
add, argtest, conman, crash, faulter, forkbomb, forktest, sty, tictac
2. 스스로 프로그램을 작성하여 테스트할 수 있다. 예를 들어 다음과 같이 수행할 수 있다.
char *filename = "/bin/cp";
char *args[4];
pid_t pid;
args[0] = "cp";
args[1] = "file1";
args[2] = "file2";
args[3] = NULL;
pid = fork();
if (pid == 0) execv(filename, argv);
3. 셸 프로그램 작성
user/bin/sh 에 아주 간단한 셸 프로그램이 있는데, 내용은 비어 있다. 이를 완성하여, sh 를 수행시키면 prompt 를 출력하고, 사용자가 명령을 입력하면 프로그램을 수행시키는 아주 간단한 셸 프로그램을 작성하라.
"exit"를 사용자가 입력하면 셸 프로그램을 종료하도록 한다.