유저모드에서 유저 프로세스가 호출한 시스템 콜이 어떻게 커널모드로 바뀌고, 어떻게 해당 시스템 콜에 해당하는 함수가 호출되는지에 대한 과정이 헷갈려서 며칠동안 찾아해멨다. 반에 똑똑한 친구들이 많아서 드디어 흐름을 이해할 수 있어서 짧게 정리해보려고 한다.
우선 유저프로그램에서 fork() 시스템콜을 호출하는 상황을 예로 들어 흐름을 설명해보려고 한다.
1. 유저프로그램에서 fork(스레드이름); 으로 fork 호출
2. fork() : syscall1(fork의 시스템콜 번호, 스레드이름); 호출
/* lib/user/syscall.c 파일 */
pid_t
fork (const char *thread_name){
return (pid_t) syscall1 (SYS_FORK, thread_name);
}
3. syscall1() : syscall(fork의 시스템 콜 번호, 스레드 이름); 호출
#define syscall1(NUMBER, ARG0) ( \
syscall(((uint64_t) NUMBER), \
((uint64_t) ARG0), 0, 0, 0, 0, 0))
4. syscall(): CPU 레지스터 %rax에 시스템 콜 번호, %rdi 에 스레드 이름을 넣고, syscall 실행
__attribute__((always_inline))
static __inline int64_t syscall (uint64_t num_, uint64_t a1_, uint64_t a2_,
uint64_t a3_, uint64_t a4_, uint64_t a5_, uint64_t a6_) {
int64_t ret;
register uint64_t *num asm ("rax") = (uint64_t *) num_;
register uint64_t *a1 asm ("rdi") = (uint64_t *) a1_;
register uint64_t *a2 asm ("rsi") = (uint64_t *) a2_;
register uint64_t *a3 asm ("rdx") = (uint64_t *) a3_;
register uint64_t *a4 asm ("r10") = (uint64_t *) a4_;
register uint64_t *a5 asm ("r8") = (uint64_t *) a5_;
register uint64_t *a6 asm ("r9") = (uint64_t *) a6_;
__asm __volatile(
"mov %1, %%rax\n"
"mov %2, %%rdi\n"
"mov %3, %%rsi\n"
"mov %4, %%rdx\n"
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n" /* syscall 실행 */
: "=a" (ret)
: "g" (num), "g" (a1), "g" (a2), "g" (a3), "g" (a4), "g" (a5), "g" (a6)
: "cc", "memory");
return ret;
}
5. syscall : syscall-entry 실행 (핀토스 부팅시 init.c에서 실행되는 syscall_init에서 syscall 호출시 syscall-entry가 실행되도록 이미 설정되어있기 때문)
#define MSR_STAR 0xc0000081 /* Segment selector msr */
#define MSR_LSTAR 0xc0000082 /* Long mode SYSCALL target */
#define MSR_SYSCALL_MASK 0xc0000084 /* Mask for the eflags */
void
syscall_init (void) {
write_msr(MSR_STAR, ((uint64_t)SEL_UCSEG - 0x10) << 48 |
((uint64_t)SEL_KCSEG) << 32);
write_msr(MSR_LSTAR, (uint64_t) syscall_entry); /* syscall ->syscall_entry 실행 */
/* The interrupt service rountine should not serve any interrupts
* until the syscall_entry swaps the userland stack to the kernel
* mode stack. Therefore, we masked the FLAG_FL. */
write_msr(MSR_SYSCALL_MASK,
FLAG_IF | FLAG_TF | FLAG_DF | FLAG_IOPL | FLAG_AC | FLAG_NT);
}
6. syscall-entry.S :
- CPU의 rsp 값에 tss에 저장된 커널스택의 스택포인터를 저장
→ 이제 rsp는 유저 스택이 아닌 커널 스택을 가리킴 = 커널모드 시작/* syscall-entry.S 부분 */ movabs $tss, %r12 movq (%r12), %r12 movq 4(%r12), %rsp /* Read ring0 rsp from the tss */
- CPU 레지스터 값들을 커널 스택에 push
: push 되는 순서는 마지막 들어간 데이터부터 interrupt frame 구조체 순서가 되도록 하는 순서로 push/* syscall-entry.S 부분 */ push $(SEL_UDSEG) /* if->ss */ push %rbx /* if->rsp */ push %r11 /* if->eflags */ push $(SEL_UCSEG) /* if->cs */ push %rcx /* if->rip */ subq $16, %rsp /* skip error_code, vec_no */ push $(SEL_UDSEG) /* if->ds */ push $(SEL_UDSEG) /* if->es */ push %rax movq temp1(%rip), %rbx push %rbx pushq $0 push %rdx push %rbp push %rdi push %rsi push %r8 push %r9 push %r10 pushq $0 /* skip r11 */ movq temp2(%rip), %r12 push %r12 push %r13 push %r14 push %r15 movq %rsp, %rdi
- 커널 스택에 인터럽트 프레임 구조체 형태의 데이터가 다 들어가 있고, rsp는 이 시작점을 가리키는 형태
→ syscall_handler() 호출 시 인자로 rsp를 넘겨주면 핸들러는 *if 를 받았다고 생각할 수 있음/* syscall-entry.S 부분 */ movabs $syscall_handler, %r12 call *%r12
7. syscall_handler (struct intr_frame *f):
인자로 들어온 f는 커널스택의 rsp이고, rsp부터는 intr_frame 형태의 데이터들이 순차적으로 들어가 있기 때문에 이 자체를 인터럽트 프레임처럼 사용할 수 있는 것 → f의 rax 값을 통해 필요한 시스템 콜을 수행하는 함수를 호출한다.
struct syscall_func syscall_func[] = {
{SYS_HALT,syscall_halt},
{SYS_EXIT,syscall_exit},
{SYS_FORK,syscall_fork},
{SYS_EXEC,syscall_exec},
{SYS_WAIT,syscall_wait},
{SYS_CREATE,syscall_create},
{SYS_REMOVE,syscall_remove},
{SYS_OPEN,syscall_open},
{SYS_FILESIZE,syscall_filesize},
{SYS_READ,syscall_read},
{SYS_WRITE,syscall_write},
{SYS_SEEK,syscall_seek},
{SYS_TELL,syscall_tell},
{SYS_CLOSE,syscall_close},
};
void
syscall_handler (struct intr_frame *f) {
struct syscall_func call = syscall_func[f->R.rax];
call.function (f);
}
나처럼 흐름이 궁금했던 사람들에게 도움이 되길 바라며.. 이만...
반응형
'TIL' 카테고리의 다른 글
[JS] 자바스크립트에서 함수 정의 - 함수 정의 이전에 함수 호출하기 (0) | 2023.04.27 |
---|---|
[OS] pintos의 interrupt frame 이란 + do_iret 함수가 하는 일 (0) | 2022.11.29 |
[자료구조] Red Black Tree (레드 블랙 트리) | 레드 블랙 트리란, 경계 노드, black-height(흑색높이), Left-Rotate(좌회전), Right-Rotate(우회전), C언어 구현 (2) | 2022.10.24 |
백준 11049. (PyPy3 1등 먹은 풀이!) 행렬체인곱셈 | Python 다이나믹 프로그래밍 (0) | 2022.10.19 |
백준 11047. 동전 0 | Python | 그리디 (0) | 2022.10.15 |
댓글