728x90
[Pintos] Project 1, 2_Argument parsing, User Program_system call_code analysis
1 ARGUMENT PASSING
*목표
명령어와 함께 들어온 인자들을 user program의 user stack에 정해진 순서에 따라 넣어준다.
*예시
- 'bin/ls -l foo bar'이 커맨드라인에 들어오면 argv[0] = 'bin/ls\0', 'argv[1] = -l\0', argv[2] = 'foo\0', argv[3] = 'bar\0'으로 parsing(문자열 분리)한 뒤에 아래와 같이 스택에 넣어준다.
- | argv[3] | ... | argv[0] | padding for 8 배수 | NULL pointer for argv[argc=4] | pointer for argv[3] | ... | pointer for argv[0] | return address = NULL |
- 그림 첨부해라!*코드1 file_name을 parsing하는 코드
2 strtok_r 함수: 문자열 파싱하는 함수char *argv[30]; int argc = 0; char *token, *save_ptr; // argc = N+1; argv = {filename, arg1, ... argN}로 parsing token = strtok_r(file_name, " ", &save_ptr); while (token != NULL) { argv[argc] = token; token = strtok_r(NULL, " ", &save_ptr); // 두번째 parsing부터는 첫번째 인자를 NULL로 한다 argc++; }
2 parsed arguments를 user stack에 넣어주는 함수char * strtok_r (char *s, const char *delimiters, char **save_ptr) { char *token; ASSERT (delimiters != NULL); ASSERT (save_ptr != NULL); /* If S is nonnull, start from it. If S is null, start from saved position. */ if (s == NULL) s = *save_ptr; ASSERT (s != NULL); /* Skip any DELIMITERS at our current position. */ while (strchr (delimiters, *s) != NULL) { /* strchr() will always return nonnull if we're searching for a null byte, because every string contains a null byte (at the end). */ if (*s == '\0') { *save_ptr = s; return NULL; } s++; } /* Skip any non-DELIMITERS up to the end of the string. */ token = s; while (strchr (delimiters, *s) == NULL) s++; if (*s != '\0') { *s = '\0'; *save_ptr = s + 1; } else *save_ptr = s; return token; }
2 SYSTEM CALLS각 system call 함수의 흐름을 정리했다- halt()- exit()- fork()```void load_userStack(char **argv, int argc, void **rspp) { // 1. Save argument strings (character by character) for (int i = argc - 1; i >= 0; i--) { int N = strlen(argv[i]); for (int j = N; j >= 0; j--) { char individual_character = argv[i][j]; // argv - i번째 스트링의 j번째 character; '\0'부터 거꾸로 시작 (*rspp)--; // (*rspp) = _if.rsp (unsigned long int: 8 byte 정수로 주소값 표현?) --> _if.rsp 1 감소 (1 byte 전 주소값으로 이동?) **(char **)rspp = individual_character; // rspp를 (char **)로 캐스팅하면, **(char **)rspp = *(char *)rsp = (char)(메모리) } // *(char **)rspp 는 (char *)형 포인터! argv[i] = *(char **)rspp; // (char *) 포인터..push this address too; 3번에 사용할 것 } // 2. Word-align padding int pad = (int)*rspp % 8; // (*rspp): _if.rsp 주소값 --> 8로 나눈 나머지 for (int k = 0; k < pad; k++) // pad만큼 주소값--; 주소 번지를 8의 배수 맞추기 위해 { (*rspp)--; // _if.rsp 주소값 -1 이동 **(uint8_t **)rspp = (uint8_t)0; // _if.rsp가 가리키는 내용물으 0으로 채움(1 byte) } // 3. Pointers to the argument strings size_t PTR_SIZE = sizeof(char *); (*rspp) -= PTR_SIZE; // _if.rsp를 8byte 내림 **(char ***)rspp = (char *)0; // NULL포인터로 채움 argv[argc] for (int i = argc - 1; i >= 0; i--) { (*rspp) -= PTR_SIZE; // 8byte 이동하면서 각 argv[i]가 저장된 스택의 주소값(char *) 기록(위-위 for문 마지막에 저장한 것) // 하고싶은 것: _if.rsp가 가리키는 곳에 argv[i] (char * 자료형) 을 기입하고 싶음 --> *(char **)_if.rsp = argv[i] 를 해주고 싶은 것! <-- 캐스팅하는 이유는 (char *)를 기록해야 하므로 **(char ***)rspp = argv[i]; // argv[i]는 (char *)이고, *rspp가 _if.rsp --> (char ***)rspp 로 캐스팅하면 // rspp는 (char ***), *rsp == _if.rsp는 (char **)형으로 캐스팅된 것..!: 원하는 결과 // 즉, *(char **)_if.rsp = argv[i] <==> **(char ***)rspp = argv[i] } // 4. Return address (*rspp) -= PTR_SIZE; **(void ***)rspp = (void *)0; // 마지막 return add도 void형 포인터 NULL 기입 }
(부모 스레드)
process_fork(thread_name, intr_frame) exit() --> thread_exit() --> do_schedule(THREAD_DYING)
halt() --> power_off()
- -> memcpy(&cur->parent_if, if_,): to pass this intr_frame down to child
- -> thread_create(, __do_fork, cur)
--> (자식 스레드) --> do_fork(cur) --> 1. 부모 스레드의 cpu context 복사(intr_frame) --> 2. page table 복사 --> sema_up(¤t->fork_sema): wake up parent process (child loaded successfully) --> do_iret(): switch to newly created process --> 종료
- -> sema_down(&child->fork_se ma): child thread가 실행되어 __do_fork에서 sema_up()하기까지 기다림
- -> return tid
fn_copy = palloc_get_page(): 파일 이름 동적할당하여 복사할 공간 확보#### - exec()
strlcpy(): 복사 후 넘겨주기(process_cleanup()에서 다 날라가나?)
process_exec(fn_copy)--> intr_frame 생성 --> process_cleanup(): kill the current context --> argument parsing(게시글 맨 위 argument pasging 부분 참고) --> load(file_name, &_if): 실행파일을 메모리에 로딩 --> load_userStack(): parsed arguments를 userstack에 올림 --> do_iret(): switch process
get_child_with_pid()#### - process_wait()
sema_down(&child->wait_sema): 자식 스레드 process_exit() 호출할때까지 멈춤
exit_status = child->exit_status : 자식 스레드의 종료 status 기록(child->exit_status는 자식 스레드가 syscall.c/exit()호출할때 변경)--> (자식 스레드) process_exit() --> ... process_cleanup() --> sema_up(&cur->wait_sema): process_wait()으로 잠든 부모 스레드 깨움 --> sema_down(&cur->free_sema): 부모 스레드가 자식 리스트 지울 때까지 기다림 <--
list_remove(): 자식 스레드 리스트에서 제거
sema_up(&child->free_sema): 자고 있는 자식 스레드 깨움--> 자식 스레드 process_exit() 종료
filesys_create(file, initial_size)#### - create()
- -> 1. dir_open_root(): 루트 디렉토리 inode를 메모리로 로딩
--> inode_oped(ROOT_DIR_SECTOR): 루트 섹터에서 inode를 읽어들임. ROOT_D_S = 1 --> 이미 열려있는 inode인지 확인 후 reopen(): open_inodes 리스트 순회 --> 아닌 경우, inode 메모리 동적할당 --> inode 초기화: open_inodes 리스트에 추가, 기본 멤버 초기화 --> disk_read(): 디스크 SECTOR에 저장된 정보를 inode->data에 저장 --> return inode --> dir_open(inode): dir 자료구조 메모리 할당 및 inode 연결
- -> 2. free_map_allocate(1, &inode_sector): 디스크에서 1 sector 할당(생성할 파일 inode 기록하기 위한 공간?)
- -> 3. inode_create(inode_sector, initial_size): file의 inode를 작성하고(메모리에서) 디스크(inode_sector)에 기록
--> disk_inode 동적할당(디스크에 쓸 자료구조) --> disk_inode멤버 초기화: 파일 크기 크기, 등 --> free_map_allocate(): 파일 크기에 해당하는 sector 수만큼 디스크 할당 + 시작 pointer update --> disk_write(): 디스크에 disk_inode 기록 --> disk_inode 동적 해제
- -> 4. dir_add(): 열려 있는 루트 디렉토리에 새로 만든 파일 아이노드 엔트리 추가하고 디스크에 기록
--> lookup(): DIR 내 동일 이름 파일 있는지 검사 --> dir->inode에서 비어있는 엔트리 찾는다: 해당 ofs(오프셋)값을 찾는다 --> dir->inode 해당 엔트리에 생성된 파일 inode정보(멤버) 기록: in_use, name, sector 등 --> inode_write_at(): 디스크에 저장
- -> 5. dir_close(): 루트 디렉토리 inode메모리 해제
--> inode_close(dir->inode): inode.open_cnt를 하나 내리고 0이 되면 비로소 메모리에서 지운다 --> list_remove(): open_inodes 리스트에서 지운다 --> 디스크에서 해당 섹터를 할당 해제한다(inode->sector: inode자체, inode->data: inode에 해당하는 파일) --> free(inode) --> free(dir)
- remove()
filesys_remove()
--> dir_open_root(): root 디렉토리 연다
--> dir_remove(): 디렉토리에서 해당 엔트를 사용안함처리하고, 아이노드 removed=true 처리
--> lookup(): 디렉토리에서 파일 이름을 가진 엔트리 찾는다(offset)
--> inode_open(): 엔트리에 해당하는 아이노드를 연다
--> e.in_use=true: 해당 엔트리 사용안함처리
--> inode_write_at(): 디렉토리의 해당 엔트리 갱신(사용안함처리했으므로)
--> inode_remove(inode): 해당 아이노드를 deleted 처리
--> inode->removed=true
--> inode_close(inode): inode->opencnt를 하나 내리고 0이 되면 메모리에서 내린다
--> dir_close(): 루트 디렉토리를 닫는다
- open()
file을 찾으려면 dir를 이용해야 한다. 이 과정에서 dir를 열어서 찾고 닫아야 한다.
filesys_open(): 파일을 연다
--> dir_open_root(): 루트 디렉토리 연다
--> dir_lookup(): 디렉토리 엔트리 검색 -> 찾은 파일의 inode를 open_inodes리스트에 추가
--> dir_close(): 루트 디렉토리 닫는다
--> file_open(inode): 메모리에 file 자료구조 할당 후 해당 파일의 inode 포인팅 및 초기화
--> file 구조체 동적 할당
--> 파일 멤버 초기화: inode와 연결
add_file_to_fdt(): 현재 실행 스레드의 FDT(file descriptor table)의 비어있는 인덱스에 파일 할당
- filesize()
find_file_by_fd(): 현재 실행 스레드의 fdt에서 fd번째 파일 찾는다
file_length(): 해당 파일의 길이(파일 -> inode -> inode->data.length)
--> inode_length(file->inode) --> inode->data.length
- seek()
find_file_by_fd(): 현재 실행 스레드의 fdt에서 fd번째 파일 찾는다
fileobj->pos = position: 다음에 읽을 곳(pos)을 변경한다. 참고로 file 시작점이 0이다.
- tell()
find_file_by_fd(): 현재 실행 스레드의 fdt에서 fd번째 파일 찾는다
file_tell(): 현재 파일에서 다음에 읽을 곳 반환
--> file->pos
- close()
find_file_by_fd(): 현재 실행 스레드의 fdt에서 fd번째 파일 찾는다
fd = 0 || fileobj == STDIN : 표준 입력인 경우, stdin_count -1
fd = 1 || fileobj == STDOUT: 표준 출력인 경우, stdout_count -1
remove_file_from_fdt(): fdTable[fd] 를 NULL로 만들어서 fdTable에서 없앰
CASE1: dupCount == 0 --> file_close
CASE2: dupCount != 0 --> dupCount -1
- read()
find_file_by_fd(): 현재 실행 스레드의 fdt에서 fd번째 파일 찾는다
CASE1: STDIN --> input_getc()로 한글자씩 버퍼에 복사
CASE2: STDOUT --> X
ELSE :
lock_acquire(rw_lock): rw 접근 하나의 스레드만?
file_read() -> inode_read_at(): read SIZE byes from INODE into BUFFER; 어렵다
lock_release(rw_lock)
- write()
find_file_by_fd(): 현재 실행 스레드의 fdt에서 fd번째 파일 찾는다
CASE1: STDIN --> X
CASE2: STDOUT --> putbuf()로 출력?
ELSE :
lock_acquire(rw_lock): rw 접근 하나의 스레드만?
file_write() -> inode_write_at(): write SIZE bytes from BUFFER into INODE; 어렵다
lock_release(rw_lock)
- dup2()
Creates 'copy' of oldfd into newfd
find_file_by_fd(oldfd): 현재 실행 스레드의 fdt에서 oldfd번째 파일 찾는다
find_file_by_fd(newfd): 현재 실행 스레드의 fdt에서 newfd번째 파일 찾는다
fileobj (oldfd 해당 파일)이 STDIN, STDOUT, 이외 경우 처리
close(): newfd번째 파일 닫기
fdt[newfd] 에 fileobj (oldfd 해당 파일) 넣기
참조 - special thanks to 노군
728x90
'정글 2기 > OS 운영체제' 카테고리의 다른 글
[pintos] Project 3_Virtual Memory(가상 메모리) (0) | 2021.10.19 |
---|---|
[three_easy_pieces] 주소 공간의 개념 요약 (0) | 2021.10.15 |
[Pintos] Project 2_User Program_system call (0) | 2021.10.14 |
[three_easy_pieces] 파일 시스템(File system)_vsfs(Very Simple File System) (0) | 2021.10.13 |
[OS운영체제] 권영진 교수님 OS 강의_2 (카이스트 전산학부) (0) | 2021.10.11 |
댓글