728x90
- 과제 관련 설명
- Process related: halt, exit, exec, wait, fork
- File related: create, remove, open, filesize, read, write, seek, tell, close
🎯 Goal
- Modified files
- threads/thread.h
- threads/thread.c
- userprog/syscall.c
- userprog/process.c
📝 Functions List
Ⅰ. Hierarchical Process Structure
- 현재 핀토스에는 프로세스간 부모-자식 관계를 명시하는 정보가 없다.
→ 부모-자식의 구분이 없어서 자식 프로세스의 정보를 알지 못해, 자식의 시작 or 종료 전에 부모 프로세스가 종료되는 현상이 발생할 수 있다. - ⭐ 프로세스의 정보에 부모와 자식 필드를 추가해야한다!
- 💡 핀토스의 프로세스는 한 개의 스레드로 구성되기 때문에
struct thread
에 필요한 필드를 정의 한다.
- 기존 핀토스의 user program 실행 상황
- 새 스레드를 생성하고 준비 큐에 들어간 뒤, process_wait로 넘어가면
return -1
로 바로 리턴되게끔 되어 있다.
- ∴ process_wait를 수정해줘야 한다.
- 부모 프로세스에서 자식 프로세스를 관리하는 방법은 리스트를 이용한다.
- 핀토스의 리스트 = 연결 리스트 방식
- 인터럽트 프레임 : 인터럽트 호출시, 이전 작업하던 컨텍스트 정보를 인터럽트 스택에 저장하는 구조체
- 부모 프로세스와 자식 프로세스 간의 실행관계
- 부모 프로세스는 자식 프로세스를 생성하고 실행한다.
- 자식-부모 스레드 관계를 구현하기 위해서는
fork
가 선행되어야한다. - 실행될 프로그램으로 인자들을 넘겨준다.
- 새 자식 프로세스의
pid
를 리턴한다. - 만약 프로그램 로드 또는 프로세스 생성 실패시,
-1
을 리턴한다. - ⭐ 부모 프로세스가
exec
시스템 콜을 호출하면, 부모 프로세스는 자식 프로세스가 생성되고 완전히 실행가능한 상태로 로드될 때까지 기다려야한다.
- 세마포어를 이용해, 부모 프로세스는 자식 프로세스의 생성과 로드를 기다린다.
- 자식 프로세스의 실행을 위해 sema_down (잠시 대기)
- 자식 프로세스의 생성, 실행 이후 다시 sema_up (부모 프로세스 다시 실행)
Ⅱ. 유저 프로그램 실행 흐름
1. main
: 메인 함수 실행
/* threads/init.c */
/* Pintos main program. */
int
main (void)
2. run_actions
: 커맨드 라인으로 들어온 명령 처리
/* threads/init.c -> main */
/* Run actions specified on kernel command line. */
run_actions (argv);
3. run_task
: argv를 파싱해서 첫 번째 인자가 “run”(프로그램 실행)이면 run_task 수행
/* threads/init.c -> run_actions */
static const struct action actions[] = {
{"run", 2, run_task},
...
}
4. process_create_initd
: 유저 프로그램이면 process_wait 수행
/* threads/init.c -> run_task */
const char *task = argv[1]; // argv[0] = "run"
...
process_wait (process_create_initd (task));
5. thread_create
: 실행 파일을 이름으로 한 새 스레드 생성
- 파일이름으로 로드된 유저 프로그램을 시작한다. 새 스레드는
process_create_initd()
가 리턴되기 전에 스케줄 되거나 종료될 수 있다. 스레드가 정상적으로 생성되었을 때는 생성된 스레드의 id를, 실패했을 때는 TID_ERROR(-1)을 리턴한다. 해당 함수는 반드시 한번만 실행되어야한다.
/* userprog/process.c -> process_create_initd */
/*
* 파일 이름으로 기존에는 커맨드 라인 전체로 들어오는데, 이때 커맨드라인을 파싱하여
* 공백을 기준으로 첫 번째 문자열(실행 파일)로 스레드가 생성되도록 바꿔준다.
*/
tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy);
return tid; // process_wait의 인자로 전달된다.
6. initd
: 유저 프로세스를 처음 실행할 때 수행된다.
/* userprog/process.c -> initd */
process_init (); // 프로세스 초기화
if (process_exec (f_name) < 0) // 해당 프로세스 실행
PANIC("Fail to launch initd\n");
7. process_wait
: 4.의 process_create_initd
으로 부터 생성된 tid 값을 가지고 wait를 수행한다.
/* threads/init.c -> run_task */
const char *task = argv[1]; // argv[0] = "run"
...
process_wait (process_create_initd (task)); // process_create_initd (task) returns tid.
Ⅲ. 프로세스 계층 구조 구현
- 부모-자식 프로세스를 표현하기 위해
struct thread
를 수정해준다.
/* threads/thread.h */
struct thread {
...
#ifdef USERPROG
...
// Ref_92p. Hanyang Univ
struct intr_frame parent_if; // 부모 프로세스의 인터럽트 프레임
struct list child_list; // 자식 프로세스 리스트
struct list_elem child_elem; // 자식 프로세스 리스트의 element
struct file *running; // 현재 실행 중인 파일
int exit_status; // 프로세스의 종료 유무 확인
struct semaphore fork_sema; // fork가 완료될 때 sema_up 수행
struct semaphore free_sema; // 자식 프로세스가 종료될 때까지 부모 프로세스는 대기
struct semaphore wait_sema; // 자식 프로세스가 종료될 때까지 대기. 종료 상태 저장
#endif
...
};
parent_if
: 자식 프로세스의 로드 및 실행 후, 다시 부모 프로세스를 실행시키기 위한 부모 프로세스의 컨텍스트 정보를 담는다.child_list
: 자식 프로세스들의 정보를 리스트에 담는다.running
: 현재 실행중인 파일을 저장한다.exit_status
: 자식 프로세스의 종료 상태를 저장한다.fork_sema
:process_fork
에서 자식 프로세스의 로드를 기다릴 때 사용한다.free_sema
:process_wait
에서 자식 프로세스의 종료 상태를 받기 위해 사용한다.wait_sema
:process_wait
에서 자식 프로세스가 종료되길 기다릴 때 사용한다.
/* threads/thread.c */
static void
init_thread (struct thread *t, const char *name, int priority) {
...
t->exit_status = 0;
t->running = NULL;
/* 자식 리스트 및 세마포어 초기화 */
list_init(&t->child_list);
sema_init(&t->wait_sema,0);
sema_init(&t->fork_sema,0);
sema_init(&t->free_sema,0);
}
- 수정한
struct thread
에 대한 초기화 작업을 수행한다. - ⭐ 이때 세마포어의 값이 “0” 임을 유의한다!
→ 부모 프로세스에서 먼저 호출하기 때문에 부모 프로세스를 대기(wait 상태) 시키기 위해선 “0”으로 만들어둬야한다.
Ⅳ. Help Functions(추가 함수)
1. get_child_process
👉 자식 리스트를 검색하여 해당 프로세스 디스크립터를 리턴한다.
/* userprog/process.c */
struct thread *get_child_process(int pid){
struct thread *curr = thread_current();
struct list *child_list = &curr->child_list;
// 자식 리스트를 순회하면서 프로세스 디스크립터 검색
for (struct list_elem *e = list_begin(child_list); e != list_end(child_list); e = list_next(e))
{
struct thread *t = list_entry(e, struct thread, child_elem);
if (t->tid == pid) // 해당 pid가 존재하면 프로세스 디스크립터 리턴
return t;
}
return NULL; // 리스트에 존재하지 않으면 NULL
}
process_fork
를 통해 부모 프로세스에서 자식 프로세스를 복제하며 부모 프로세스의child_list
에 자식 프로세스의 element 값을 넣어준다.- 이후,
process_wait
과process_fork
의load
과정 이전에tid
로 받은 프로세스가 실제 부모의 자식 프로세스가 맞는지 검증하는 과정을 거친다. - [Hanyang Univ] ‘get_child_process’ 함수
Ⅴ. 프로세스 관련 시스템 콜 구현
- 기존 프로세스 구조는 부모/자식 구분 x → 자식 시작 종료 전에 부모 프로세스가 종료되는 현상 발생
- 📢 프로세스 계층 구조 구현 필요!
- struct thread에 부모와 자식 필드를 추가하고 이를 관리하는 함수를 구현한다.
- 부모 프로세스를 가리키는 포인터 추가
- 자식 프로세스 : 리스트로 구현
- 자식 리스트에서 원하는 프로세스를 검색, 삭제하는 함수 구현
1. fork
- 현재 프로세스의 복제본으로 THREAD_NAME이라는 이름을 가진 새 프로세스를 생성한다.
- ⭐ 자식 프로세스에 부모 프로세스의 %RBX, %RSP, %RBP, %R12 ~ %R15 레지스터 값을 복사한다.
- 부모 프로세스는 자식 프로세스의 pid를 리턴한다.
- 자식 프로세스는 0을 리턴한다.
- 자식 프로세스는 부모 프로세스의 파일 디스크립터와 가상 메모리 공간을 복제해야한다.
- ⭐ 부모 → 자식 : 파일 디스크립터 테이블, 페이지 테이블 복제
- 부모 프로세스는 자식 프로세스가 성공적으로 복제되었는지를 알기 전까지 리턴되면 안된다.
- 자식 프로세스가 복제를 실패하면 TID_ERROR(-1)을 리턴한다.
threads/mmu.c
에 구현된pml4_for_each()
는 페이지 테이블 구조를 포함한 전체 사용자 메모리 공간을 복사하지만 전달된 함수 중pte_for_each_func
의 누락된 부분을 채워야한다.
1. fork(system call)
/* userprog/syscall.c */
pid_t fork (const char *thread_name, struct intr_frame *f) {
check_address(thread_name);
return process_fork(thread_name, f);
}
- ⭐ “Gitbook”과 “lib/user/syscall.h”에 정의된 fork 시스템 콜의 원형은
pid_t fork (const char *thread_name);
형태이지만 “userprog/process.c” 에 구현된 process_fork 의 형태가tid_t process_fork (const char *name, struct intr_frame *if_);
이므로 fork의 두 번째 인자로intr_frame
를 추가해준다.
2. process_fork
/* userprog/process.c */
tid_t
process_fork (const char *name, struct intr_frame *if_ UNUSED) {
/* Clone current thread to new thread.*/
struct thread *curr = thread_current();
memcpy(&curr->parent_if, if_, sizeof(struct intr_frame)); // 전달받은 intr_frame을 parent_if필드에 복사한다.
// ↳ '__do_fork' 에서 자식 프로세스에 부모의 컨텍스트 정보를 복사하기 위함(부모의 인터럽트 프레임을 찾는 용도로 사용)
tid_t tid = thread_create(name, PRI_DEFAULT, __do_fork, curr); // __do_fork를 실행하는 스레드 생성, 현재 스레드를 인자로 넘겨준다.
if (tid == TID_ERROR)
return TID_ERROR;
struct thread *child = get_child_process(tid);
sema_down(&child->fork_sema); // 자식 프로세스가 로드될 때까지 부모 프로세스는 대기한다.
if (child->exit_status == TID_ERROR)
return TID_ERROR;
return tid; // 부모 프로세스의 리턴값 : 생성한 자식 프로세스의 tid
}
- curr : 현재 실행 중인 프로세스(부모)
- memcpy
- 현재 실행 중인 프로세스의 컨텍스트 정보를 담은 intr_frame를 parent_if로 복사한다.
- 🙋♂️ 부모 프로세스(curr)의 필드 중
parent_if
에if_
를 복사한다?
→thread_create
에서 넘겨주는 4번째 인자(가curr
인데, 이는 다시__do_fork
의 첫 번째 인자로 사용된다.
→ 해당 함수(__do_fork
) 에서 memcpy로 복사한&curr->parent_if
를 꺼내와서 다시 자식 프로세스의 인터럽트 프레임에 복사한다.(아래 설명 참조)
😵💫 실행 순서가 제대로 이해가 안돼서 코드 한 줄 한 줄 어떤 식으로 진행되는지 함수 타고 들어가기를 반복했다.
⭐ 실행 순서 함수명 보고 잘 따라오기!!
3. thread_create
🎪 우선 thread_create
함수 내부로 진입한다.
/* threads/thread.c */
tid_t
thread_create (const char *name, int priority, thread_func *function, void *aux) {
struct thread *t;
tid_t tid;
...
/* 현재 스레드의 자식 리스트에 새로 생성한 스레드 추가 */
struct thread *curr = thread_current();
list_push_back(&curr->child_list,&t->child_elem);
/* Call the kernel_thread if it scheduled.
* Note) rdi is 1st argument, and rsi is 2nd argument. */
t->tf.rip = (uintptr_t) kernel_thread;
t->tf.R.rdi = (uint64_t) function;
t->tf.R.rsi = (uint64_t) aux;
t->tf.ds = SEL_KDSEG;
t->tf.es = SEL_KDSEG;
t->tf.ss = SEL_KDSEG;
t->tf.cs = SEL_KCSEG;
t->tf.eflags = FLAG_IF;
...
return tid;
}
- 실질적인 자식 프로세스를 생성하는 부분으로, 현재
fork
를 호출한 프로세스(running thread)가 부모 프로세스이므로 해당 프로세스의 자식 리스트에 새로 생성한 자식 프로세스(struct thread *t
)를 추가한다. - 자식프로세스를 생성하고 난 이후의 작업을 수행하기 위해 레지스터
%rdi
와%rsi
값을 설정해준다.- 💡 RSI(Extended Source Index) / RDI(Extended Destination Index) : 확장 소스 인덱스, 확장 목적지 인덱스 레지스터. 각각 메모리 출발지와 목적지를 나타낸다.
⭐ process_fork
내부의 동작과정
tid_t tid = thread_create(name, PRI_DEFAULT, __do_fork, curr);
thread_create
로 자식 프로세스를 생성하고 tid를 리턴받는다.- Point 1. 이때, 인자로
__do_fork
함수와curr
을 함께 넘겨줬다. - Point 2.
thread_create
내부에서%rdi
로__do_fork
를 지정해줬다.
- Point 1. 이때, 인자로
thread_create
이후 부분들을 수행한다.sema_down(&child->fork_sema);
- 해당 자식 프로세스가 성공적으로 로드될 때까지 부모 프로세스는 대기한다.
threads/thread.c -> init_thread
에서fork_sema
의 값을 0으로 설정해줬으므로 부모 프로세스는 wait_list로 들어가게된다.- ⭐ 이후 자식 프로세스가 로드된 후, sema_up을 한 뒤에서야 해당 부모 프로세스가 Ready 상태로 전환된다!
- 실행중인 프로세스가 자식 프로세스로 전환이 됐으므로, 이제서야
__do_fork
를 수행한다.%rsi
에curr
(aux, 부모 프로세스)를,%rdi
에__do_fork
(function)를 넣어준 결과이다.
→curr
프로세스가 실행을 마친 뒤,__do_fork
가 실행된다.
- 실제로 print문을 넣어, TC ‘fork-read’를 돌려보면서 함수의 실행 순서를 파악해봤다.
Executing 'fork-read':
thread_create calls!
(fork-read) begin
(fork-read) open "sample.txt"
process_fork calls! # 1. process_fork 호출
thread_create calls! # 2. thread_create 호출
before sema_down(fork_sema)! # 3. sema_down 수행
__do_fork calls! # 4. __do_fork 호출
(fork-read) child run
(fork-read) Child: pintos is funny!
(fork-read) end
child: exit(0)
(fork-read) Parent success
(fork-read) end
fork-read: exit(0)
Execution of 'fork-read' complete.
4. __do_fork
/* userprog/process.c */
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;
bool succ = true;
parent_if = &parent->parent_if; // process_fork에서 복사 해두었던 intr_frame
/* 1. Read the cpu context to local stack. */
memcpy (&if_, parent_if, sizeof (struct intr_frame));
if_.R.rax = 0; // fork 시스템 콜의 결과로 자식 프로세스는 0을 리턴해야하므로 0을 넣어준다.
/* 2. Duplicate PT */
current->pml4 = pml4_create(); // 부모의 pte를 복사하기 위해 페이지 테이블을 생성한다.
if (current->pml4 == NULL)
goto error;
process_activate (current);
#ifdef VM
supplemental_page_table_init (¤t->spt);
if (!supplemental_page_table_copy (¤t->spt, &parent->spt))
goto error;
#else
// "pml4_for_each" : Apply FUNC to each available pte entries including kernel's.
if (!pml4_for_each (parent->pml4, duplicate_pte, parent)) // "duplicate_pte" : 페이지 테이블을 복제하는 함수(부모 -> 자식)
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.*/
/*
* 파일 객체를 복제하려면 'file_duplicate'를 사용하라.
* 이 함수가 부모의 리소스를 성공적으로 복제할 때까지 부모 프로세스는 fork로 부터 리턴할 수 없다.
*/
if (parent->next_fd == FDCOUNT_LIMIT)
goto error;
// 부모의 fdt를 자식의 fdt로 복사한다.
for (int fd = 2; fd < FDCOUNT_LIMIT; fd++) {
struct file *file = parent->fdt[fd];
if (file == NULL) // fd엔트리가 없는 상태에는 그냥 건너뛴다.
continue;
current->fdt[fd] = file_duplicate (file);
}
current->next_fd = parent->next_fd; // 부모의 next_fd를 자식의 next_fd로 옮겨준다.
sema_up(¤t->fork_sema); // fork가 정상적으로 완료되었으므로 현재 wait중인 parent를 다시 실행 가능 상태로 만든다.
/* Finally, switch to the newly created process. */
if (succ)
do_iret (&if_);
error: // 제대로 복제가 안된 상태 - TID_ERROR 리턴
sema_up(&parent->fork_sema);
exit(TID_ERROR);
// thread_exit (); // origin_code
}
__do_fork
의 인자로thread_create
를 하면서 4번째 인자로 넣은curr
(aux) 가 들어가게 된다.
- 자식 프로세스에 부모의 인터럽트 프레임(실행 컨텍스트)를 복사해서 넣어준다.
- 부모 프로세스의 페이지 테이블을 자식 프로세스의 페이지 테이블로 복제한다.
(duplicate_pte
_”filesys/filesys.h”) 사용 - 부모의 fdt와 next_fd를 복사하여 자식 프로세스에게 설정해준다.
- 이후, 부모 프로세스에서 자식 프로세스로의 모든 복사 과정이 완료되었으므로
sema_up
을 수행한다.- ⭐ 반드시
process_fork
에서의 세마포어와 동일한 세마포어에 대해 Up 연산을 수행해주어야한다!
- ⭐ 반드시
do_iret (&if_);
:sema_up
을 진행해서 부모 프로세스가 다시 Ready 상태가 되었으므로 컨텍스트 스위치를 수행한다.
2. wait
- 자식 프로세스(pid)를 기다리고 자식의 종료 상태(exit_status)를 가져온다.
- 만약 아직 자식(pid)이 살아있으면 종료될 때까지 기다린다.
- 만약 자식(pid)이 exit()를 호출하지 않고 커널에 의해 종료된다면 wait는 -1을 리턴해야한다.
- 부모 프로세스가 이미 종료된 자식 프로세스를 기다리는 건 합당하지만 커널은 부모에게 자식의 종료 상태 또는 커널에 의해 종료되었다는 사실을 알려야한다.
- wait 가 -1을 리턴해야하는 상황
- 자식(pid)는 호출하는 부모 프로세스의 직속 자식을 참조하지는 않는다.
- fork 호출 후, 성공적으로 pid를 반환받은 경우에만 직속 자식이다.
- 자식들은 상속되지 않는다.
- 만약 부모 자식 관계가 아래와 같다면 (’p’ : parent, ‘c’ : child)
- A(p) → B(c)
- B(p) → C(c)
A(p) → C(c)는 성립하지 않는다. - ⭐ 프로세스 A가 wait(C)를 호출하는 건 실패해야한다.*
- ⭐ 프로세스 B가 죽은 경우에도 C를 A에 할당하지 않는다!*
- 한 프로세스는 어느 주어진 자식에 대해 최대 한번만 wait 를 할 수 있다.
- 자식(pid)는 호출하는 부모 프로세스의 직속 자식을 참조하지는 않는다.
- 프로세스는 자식을 얼마든지 낳을 수 있고 자식들은 어떤 순서로도
wait
할 수 있다.자식 전부에 대해 기다리지 않고 종료하기도 한다. wait
가 발생할 수 있는 모든 방법에 대해 고려하고 설계해야한다.- 모든 프로세스의 자원들(eg.
struct thread
)은 부모가 해당 프로세스를 기다리던지, 부모가 먼저 또는 나중에 종료되던지에 관계없이 해제되어야한다.
- 초기 프로세스가 종료되기 전에 pintos가 종료되지 않도록 해야한다.
- 💡
wait
구현전에는 무한루프로 구현하여, 프로세스가 종료되지 않도록한다.
- 💡
1. wait (systemcall)
/* userprog/syscall.c */
int wait (tid_t pid){
process_wait(pid);
}
- 대기하려는 자식 프로세스의 pid를 인자로 넘겨준다.
2. process_wait
/* userprog/process. */
process_wait (tid_t child_tid UNUSED) {
struct thread *child = get_child_process(child_tid);
if(child == NULL) // 해당 자식이 존재하지 않는다면 -1 리턴
return -1;
sema_down(&child->wait_sema); // 자식 프로세스가 종료할 때까지 대기한다.
// 컨텍스트 스위칭 발생
int exit_status = child->exit_status; // 자식으로 부터 종료인자를 전달 받고 리스트에서 삭제한다.
list_remove(&child->child_elem);
sema_up(&child->free_sema); // 자식 프로세스 종료 상태를 받은 후 자식 프로세스를 종료하게 한다.
return exit_status;
}
- 해당 자식 프로세스가 성공적으로 종료될 때까지 부모 프로세스는 대기한다.
threads/thread.c -> init_thread
에서wait_sema
의 값을 0으로 설정해줬으므로 부모 프로세스는 wait_list로 들어가게된다.- ⭐ 이후 자식 프로세스가 종료(
process_exit
)된 후, sema_up을 한 뒤에서야 해당 부모 프로세스가 Ready 상태로 전환된다!
- 자식 프로세스가 성공적으로 종료됐다면 부모 프로세스의 자식 프로세스 리스트에서 해당 자식 프로세스를 제거한다.
free_sema
는 자식 프로세스의 종료 상태를 받고 자식 프로세스를 종료해야하므로 세마포어를 이중적으로 걸어줬다고 보면 된다.
/* userprog/process. */
void
process_exit (void) {
struct thread *curr = thread_current ();
for (int i = 0; i < FDCOUNT_LIMIT; i++) // 프로세스 종료 시, 해당 프로세스의 fdt의 모든 값을 0으로 만들어준다.
close(i);
palloc_free_multiple(curr->fdt, FDT_PAGES); // fd table 메모리 해제
file_close(curr->running); // 현재 프로세스가 실행중인 파일을 종료한다.
process_cleanup ();
sema_up(&curr->wait_sema); // 부모 프로세스가 자식 프로세스의 종료상태를 확인하게 한다.
sema_down(&curr->free_sema); // 부모 프로세스가 자식 프로세스의 종료 상태를 받을때 까지 대기한다.
}
- 자식 프로세스의 종료 시, 부모 프로세스에게 자식 프로세스의 종료를 알릴 수 있게끔 세마포어 연산을 수행한다.
3. exec
- 현재 실행중인 프로세스를 cmd_line 에 지정된 실행 파일로 변경하고 인수들을 전달한다.
- return
- 성공 시, 리턴 x [Hanyang Univ : pid_t return]
- 실패 시, -1
- 해당 함수는 exec 를 호출한 스레드의 이름을 변경하지는 않는다.
- 파일 디스크립터는 exec 호출 시, 열린 상태로 존재한다.
1. exec(system call)
/* userprog/syscall.c */
int exec (const char *cmd_line){
check_address(cmd_line);
int size = strlen(cmd_line) + 1; // 파일 사이즈(NULL 포함하기 위해 +1)
char *fn_copy = palloc_get_page(PAL_ZERO);
if (fn_copy == NULL)// 메모리 할당 불가 시
exit(-1);
strlcpy(fn_copy, cmd_line, size);
if (process_exec(fn_copy) == -1) // [process_exec] 'load (file_name, &_if);' -> load 실패 시
return -1;
return 0;
}
2. process_exec
/* userprog/process.c */
int
process_exec (void *f_name) {
char *file_name = f_name; // 커맨드 라인 전체가 인자로 들어온다.
bool success;
struct intr_frame _if;
_if.ds = _if.es = _if.ss = SEL_UDSEG;
_if.cs = SEL_UCSEG;
_if.eflags = FLAG_IF | FLAG_MBS;
int argc = 0;
char *argv[128]; // 64bit computer(uint64_t : 8byte)
/* We first kill the current context */
process_cleanup ();
/* 커맨드 라인을 파싱한다. */
argument_parse(file_name, &argc, argv);
/* And then load the binary */
success = load (file_name, &_if);
/* If load failed, quit. */
if (!success){
palloc_free_page (file_name);
return -1;
}
argument_stack(argc, argv, &_if); // argc, argv로 커맨드 라인 파싱
// hex_dump(_if.rsp, _if.rsp, USER_STACK - _if.rsp, true); // 메모리에 적재된 상태 출력
/* Start switched process. */
do_iret (&_if);
NOT_REACHED ();
}
- 커맨드 라인 전체를 인자로 받았으므로, 커맨드 라인 문자열에 대한 파싱을 진행해, 실행파일(argv[0])을 분리해낸다.
/* userprog/process.c */
static bool
load (const char *file_name, struct intr_frame *if_) {
struct thread *t = thread_current ();
struct ELF ehdr;
struct file *file = NULL;
off_t file_ofs;
bool success = false;
int i;
...
t->running = file;
file_deny_write(file); // 현재 오픈한 파일에 접근 못하게 함
...
return success;
}
exec
를 통해 파일을 실행하게 되면 해당 프로세스에 해당하는 파일은 열린 상태로 있으므로struct thread
의 running 필드에 해당 파일을 추가한다.process_exit
에서 해당 파일에 대해 닫아주는 작업을 수행한다.- 또, 현재 실행하기위해 오픈한 파일에 대해 접근하지 못하게 하기 위해
file_deny_write
를 이용해 접근 제한을 걸어준다.
- 또, 현재 실행하기위해 오픈한 파일에 대해 접근하지 못하게 하기 위해
728x90
'🌱 Dev Diary > 📄 TIL' 카테고리의 다른 글
Project 3. Memory Management (0) | 2023.01.19 |
---|---|
Project 3. Introduction (0) | 2023.01.19 |
Project 2. System Calls 구현 (2) - File related (0) | 2023.01.02 |
Project 2. System Calls 구현 (1) - 개요 (0) | 2023.01.02 |
Project 1. Priority Scheduling 구현 (2) (0) | 2022.12.28 |