Setup the argument for user program in process_exec()
- 해당 기능을 구현하기 전에는 새로운 프로세스에 인자를 넘겨주지 못한다.
ex) 'echo x' 실행시 'echo'라는 프로그램에 'x'라는 인자를 넘겨주어야 하지만 현재 상태는 'echo x'라는 프로그램을 실행시킴
PintOS에서 argument passing
- /bin/ls -l foo bar 입력시 argument passing 예
- 유저 프로그램을 실행시킬때 필요한 인자들을 넘겨주기 위해 rsp(코드에선 esp)를 감소시키면서 입력받은 인자들을 스택에 넣어주는 작업
- argument는 오른쪽부터 스택에 넣는다
- word-align을 위해 8byte 단위로 정렬한다
- argv의 주소입력이 끝났다는 것을 알려주기 위해 0 삽입
- argv 인자들의 주소 입력
- fake "return address" 추가
- argc와 argv 시작 주소를 레지스터에 삽입
Implement system call infrastructure
PintOS에서 system call 처리의 대략적인 흐름
: ) | Register |
---|---|
system call number | %rax |
argument 1 | %rdi |
argument 2 | %rsi |
argument 3 | %rdx |
argument 4 | %r10 |
... | ... |
- System Call 호출시 레지스터에 입력되는 값
void check_address(void *addr) {
struct thread *cur = thread_current();
if (addr == NULL || is_kernel_vaddr(addr) || pml4_get_page(cur->pml4, addr) == NULL)
exit(-1);
}
3가지 상황을 확인하여 하나라도 해당하면 exit(-1) 호출
void halt(void) {
// * power_off()를 사용하여 pintos 종료
power_off();
}
- exit() syscall 호출
- 프로세스 종료 메시지 출력 후 thread_exit() 호출
- thread_exit()에서 process_exit()을 호출
- process_exit()에서 열려있던 파일들을 다 닫고 부모의 exit_sema UP 신호를 기다림
- 부모가 exit_sema를 up해주면 fdt에 할당받은 페이지들을 반납하고 종료
void exit(int status) {
/*
* 실행중인 스레드 구조체를 가져와 exit_status 값 수정
* 프로세스 종료 메시지 출력
* 출력 양식: "프로세스 이름: exit(종료상태)"
* thread 종료
*/
struct thread *cur = thread_current();
cur->exit_status = status;
printf("%s: exit(%d)\n", cur->name, status);
thread_exit();
}
/* Exit the process. This function is called by thread_exit (). */
void
process_exit (void) {
struct thread *curr = thread_current ();
struct file **table = curr->fdt;
/* TODO: Your code goes here.
* TODO: Implement process termination message (see
* TODO: project2/process_termination.html).
* TODO: We recommend you to implement process resource cleanup here.
*/
if (curr->run_file)
file_close(curr->run_file);
int cnt = 2;
while (cnt < 128) {
if (table[cnt]) {
file_close(table[cnt]);
table[cnt] = NULL;
}
cnt++;
}
struct list_elem *e;
struct thread *ch;
sema_up(&curr->load_sema);
// * 부모 thread로 부터 죽어도 된다는 신호가 올때까지 대기하고 있어야 함
sema_down(&curr->exit_sema);
palloc_free_page(table);
process_cleanup ();
}
- fork() syscall 호출
- syscall handler로 넘겨받은 intr_fram f를 현재 스레드의 ptf에 저장
- process_fork() 함수를 통해 자식 스레드 생성 후 자식 스레드는 __do_fork() 함수 실행
- 자식 스레드 if에 syscall handler에서 저장했던 부모 스레드의 ptf를 넘김
- 자식 스레드의 fork 리턴값은 0이므로 'if_R.rax = 0'
- 부모 스레드의 파일테이블을 모두 복사
int fork (const char *thread_name) {
check_address(thread_name);
return process_fork(thread_name, &thread_current()->ptf);
}
tid_t
process_fork (const char *name, struct intr_frame *if_ UNUSED) {
/* Clone current thread to new thread.*/
struct thread *cur = thread_current();
tid_t ctid = thread_create (name, PRI_DEFAULT, __do_fork, cur);
if (ctid == TID_ERROR)
return TID_ERROR;
sema_down(&cur->fork_sema);
return ctid;
}
fork()에서 intr_frame에 관한 더 자세한 내용은 Debug Choi 블로그를 참고하면 된다.
bool create (const char *file, unsigned initial_size) {
/*
* 파일 이름과 크기에 해당하는 파일 생성
* 파일 생성 성공 시 true 반환, 실패 시 false 반환
*/
check_address(file);
return filesys_create(file, initial_size);
}
bool remove (const char *file) {
/*
* 파일 이름에 해당하는 파일을 제거
* 파일 제거 성공 시 true 반환, 실패 시 false 반환
*/
check_address(file);
return filesys_remove(file);
}
- open() syscall 호출
- filesys_open 함수를 통해 파일을 열어 fd에 저장
- 현재 스레드의 fdt에 추가하고 fdt에 저장된 파일이 128개가 넘었다면 다시 close (더 이상 파일 open이 불가능한 상태)
- 만약 파일 열기에 실패했다면 -1을 리턴
int open (const char *file) {
check_address(file);
struct thread *cur = thread_current();
struct file *fd = filesys_open(file);
if (fd) {
for (int i = 2; i < 128; i++) {
if (!cur->fdt[i]) {
cur->fdt[i] = fd;
return i;
}
}
file_close(fd);
}
return -1;
}
int filesize (int fd) {
struct file *file = thread_current()->fdt[fd];
if (file)
return file_length(file);
return -1;
}
- read() syscall 호출
- stdin에 read를 요청시 input_getc() 함수 활용
- 그 외 파일은 file_read 함수를 통해 파일에서 버퍼 사이즈 만큼 read
- 다른 프로세스가 파일을 사용하고 있을 수 있으니 lock을 활용
- 만약 파일 읽기에 실패했다면 -1을 리턴
int read (int fd, void *buffer, unsigned size) {
check_address(buffer);
if (fd == 1) {
return -1;
}
if (fd == 0) {
lock_acquire(&filesys_lock);
int byte = input_getc();
lock_release(&filesys_lock);
return byte;
}
struct file *file = thread_current()->fdt[fd];
if (file) {
lock_acquire(&filesys_lock);
int read_byte = file_read(file, buffer, size);
lock_release(&filesys_lock);
return read_byte;
}
return -1;
}
int write (int fd UNUSED, const void *buffer, unsigned size) {
check_address(buffer);
if (fd == 0) // STDIN일때 -1
return -1;
if (fd == 1) {
lock_acquire(&filesys_lock);
putbuf(buffer, size);
lock_release(&filesys_lock);
return size;
}
struct file *file = thread_current()->fdt[fd];
if (file) {
lock_acquire(&filesys_lock);
int write_byte = file_write(file, buffer, size);
lock_release(&filesys_lock);
return write_byte;
}
}
int exec (const char *file_name) {
check_address(file_name);
int file_size = strlen(file_name) + 1;
char *fn_copy = palloc_get_page(PAL_ZERO);
if (!fn_copy) {
return -1;
}
strlcpy(fn_copy, file_name, file_size);
if (process_exec(fn_copy) == -1) {
return -1;
}
}
void seek (int fd, unsigned position) {
struct file *curfile = thread_current()->fdt[fd];
if (curfile)
file_seek(curfile, position);
}
unsigned tell (int fd) {
struct file *curfile = thread_current()->fdt[fd];
if (curfile)
return file_tell(curfile);
}