일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 파이썬
- unix i/o
- 프로그래머스
- VariableScope
- 시스템 수준 입출력
- 스택
- 클래스
- 금융데이터분석가
- 파이썬정렬
- 국내주식마감시황
- 금융데이터서비스분석가
- 금융데이터분석
- 서울디지털인재개발원
- LV1
- 파이썬 함수
- 파이썬 알고리즘
- 코스닥
- adx
- Ta-Lib
- 큐
- bigo
- 자료구조
- 차별화장세
- 코스피
- magic method
- 퀀트매매
- pml4
- 인스턴스변수
- talib
- 파이썬 자료구조
- Today
- Total
IT Studying
WEEK 08 카이스트 정글 PINTOS WIL(USER PROGRAM) 본문
이번 주는 저번 주만큼 집중이 잘 되지 않았다. 그래서 이번 주 내용이었던 시스템 콜에 대해서 간단히 정리하고 다음 주에 구현하겠지만, userprogram이랑 연관이 큰 pml4에 대해서 공부한 내용을 WIL에 정리하고자 한다.
시스템 콜
1. 시스템 콜이란?
시스템 콜은 사용자 프로그램이 운영 체제의 서비스를 요청하는 메커니즘이다. 사용자 모드에서 실행 중인 프로그램이 커널 모드의 기능(직접 컴퓨터 리소스에 접근, 수정 등)에 접근할 수 있게 한다. 사용자 모드에서 컴퓨터 리소스에 커널을 거치지 않고 직접 접근하게 되면 프로그램 복잡도도 증가하고, 유저프로그램이 컴퓨터에 무슨 짓을 할 지도 모르니 보안에도 좋지 않다. OS가 이런 부분을 대신하는 것이다.
운영 체제는 두가지 모드로 동작하는데, 유저 모드와 커널 모드이다. 컴퓨터 리소스는 커널 모드에서만 접근이 가능해 시스템 콜을 할 경우 cpu가 커널 모드로 바뀌게 된다. 그리고 요청된 시스템 콜을 적절히 수행한 후 결과를 반환한다.
2. 시스템 콜의 종류 : open(), read(), fork(), exec(), mmap(), socket(), sleep()...
3. 시스템 콜의 중요성 : 자원 관리, 추상화(하드웨어 자원의 인터페이스), 보안
4. 시스템 콜의 실행 과정.
1) 사용자 프로그램의 open 호출
2) 사용자 프로그램에서 시스템 콜 호출 시 lib/user/syscall.c 파일의 시스템 콜 인터페이스를 먼저 거침
3) 인자가 하나인 시스템 콜을 처리하는 syscall1함수 호출. int 0x30을 사용하여 소프트웨어 인터럽트 발생
4) eax 레지스터에 시스템 콜 번호, 나머지 인자는 다른 레지스터에 저장하여 커널로 전달
5) syscall_handler가 해당 정보를 통해 실행할 함수(여기서는 sys_open)결정하고 실행 결과 반환
/* lib/user/syscall.c */
int open (const char *file)
{
return syscall1 (SYS_OPEN, file);
}
/* userprog/syscall.c */
void syscall_handler (struct intr_frame *f)
{
int syscall_number = f->R.rax;
switch (syscall_number)
{
case SYS_OPEN:
f->R.rax = sys_open ((const char *) f->R.rdi);
break;
/* Other cases for different system calls */
}
}
/* userprog/syscall.c */
int sys_open (const char *file)
{
// 실제로 파일을 여는 작업 수행
// 파일 디스크립터 반환
}
PML4
1. 페이지 테이블이란?
페이지 테이블은 가상 주소와 물리 주소를 매핑한 테이블이다. 이를 통해 각 프로세스에서 필요한 메모리를 가상 주소를 이용해서 실제 물리 주소에 접근할 수 있도록 한다. 가상 메모리 시스템을 사용하는 이유는 다음과 같다.
1) 각 프로세스간 주소 공간 분리
- 각 프로세스는 고유의 가상 주소 공간을 가져 마치 하나의 컴퓨터에 자신만 있는 것처럼 착각한다. 실제 물리 메모리에 메모리가 어떻게 저장되든, 프로세스 입장에서는 가상 주소 공간에 저장 및 삭제를 마음대로 할 수 있다. 이를 통해 각 프로세스간 분리를 할 수 있고, 프로그램 입장에서 주소 공간을 편하게 사용할 수 있다.
2) 물리 메모리 용량 한계 극복
- 가상 주소를 사용하면 이론적으로는 2**64까지의 물리 주소를 사용할 수 있다. 실제 우리가 사용하는 RAM이 16기가,32기가 정도인 것을 감안하면 매우 큰 공간이다. 물론 어딘가에 실제로 저장되어 있어야해서 하드디스크의 스왑공간까지가 한계지만 16기가 램을 이용해서 몇 백 테라의 프로그램을 돌릴 수가 있다는 것이다.
3) 컴퓨터 보호
- 프로그램이 직접적인 물리 주소에 접근하지 못하도록 함으로써 프로그램은 각 정보가 어디에 저장되어 있는지 알 수 없다.
2. pml4
페이지 테이블은 계층화 되어 있다. 보통 4kb의 페이지를 사용하는데 쓰레드에 자신의 pml4테이블의 위치 정보를 저장한다. 가상주소는 각 계층별 테이블에서 접근할 인덱스고 해당 인덱스에는 참고할 물리 주소가 들어있다. 마지막 pt에 실제 접근할 물리주소가 있고, 해당 물리주소와 가상주소의 VPO를 결합하면 실제 물리주소를 접근해서 데이터를 가져올 수 있다.
static uint64_t *
pgdir_walk (uint64_t *pdp, const uint64_t va, int create) {
int idx = PDX (va);
if (pdp) {
uint64_t *pte = (uint64_t *) pdp[idx];
if (!((uint64_t) pte & PTE_P)) {
if (create) {
uint64_t *new_page = palloc_get_page (PAL_ZERO);
if (new_page)
pdp[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P;
else
return NULL;
} else
return NULL;
}
return (uint64_t *) ptov (PTE_ADDR (pdp[idx]) + 8 * PTX (va));
}
return NULL;
}
static uint64_t *
pdpe_walk (uint64_t *pdpe, const uint64_t va, int create) {
uint64_t *pte = NULL;
int idx = PDPE (va);
int allocated = 0;
if (pdpe) {
uint64_t *pde = (uint64_t *) pdpe[idx];
if (!((uint64_t) pde & PTE_P)) {
if (create) {
uint64_t *new_page = palloc_get_page (PAL_ZERO);
if (new_page) {
pdpe[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P;
allocated = 1;
} else
return NULL;
} else
return NULL;
}
pte = pgdir_walk (ptov (PTE_ADDR (pdpe[idx])), va, create);
}
if (pte == NULL && allocated) {
palloc_free_page ((void *) ptov (PTE_ADDR (pdpe[idx])));
pdpe[idx] = 0;
}
return pte;
}
/* Returns the address of the page table entry for virtual
* address VADDR in page map level 4, pml4.
* If PML4E does not have a page table for VADDR, behavior depends
* on CREATE. If CREATE is true, then a new page table is
* created and a pointer into it is returned. Otherwise, a null
* pointer is returned. */
uint64_t *
pml4e_walk (uint64_t *pml4e, const uint64_t va, int create) {
uint64_t *pte = NULL;
int idx = PML4 (va);
int allocated = 0;
if (pml4e) {
uint64_t *pdpe = (uint64_t *) pml4e[idx];
if (!((uint64_t) pdpe & PTE_P)) {
if (create) {
uint64_t *new_page = palloc_get_page (PAL_ZERO);
if (new_page) {
pml4e[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P;
allocated = 1;
} else
return NULL;
} else
return NULL;
}
pte = pdpe_walk (ptov (PTE_ADDR (pml4e[idx])), va, create);
}
if (pte == NULL && allocated) {
palloc_free_page ((void *) ptov (PTE_ADDR (pml4e[idx])));
pml4e[idx] = 0;
}
return pte;
}
/* Creates a new page map level 4 (pml4) has mappings for kernel
* virtual addresses, but none for user virtual addresses.
* Returns the new page directory, or a null pointer if memory
* allocation fails. */
uint64_t *
pml4_create (void) {
uint64_t *pml4 = palloc_get_page (0);
if (pml4)
memcpy (pml4, base_pml4, PGSIZE);
return pml4;
}
위 코드는 pml4가 생성되고 가상주소가 들어입력되었을 때 pte의 가상주소를 타고들어가면서 찾아들어가는 코드이다. 해당 가상주소에 들어있는 물리주소에서 데이터를 꺼내올 수도 있고, 해당 가상주소에 물리주소를 기록해둘 수 있다. 그리고 물리주소의 마지막 12비트의 경우 페이지단위로 저장되기 때문에 사용되지 않는데, 이것을 이용해 dirty, 읽기 쓰기 권한 같은 정보들을 기록해둔다. 실제로 물리주소를 찾아들어갈 때는 가상주소의 마지막 12비트를 이용해서 offset을 찾는다.
'카이스트 정글 > 개발일지' 카테고리의 다른 글
WEEK11 PINTOS WIL - VM (0) | 2024.10.22 |
---|---|
WEEK07 카이스트 정글 PINTOS WIL (0) | 2024.10.01 |
WEEK2 개발일지 (0) | 2024.08.19 |
WEEK1 간단한 웹개발 + 알고리즘(재귀, 정렬, 완전 탐색) (0) | 2024.08.11 |