-
[Jungle] Week10 Pintos_Project2 WILPintos 2023. 6. 12. 03:30
Argument Passing
argument passing에서 가장 중요한 부분은 사용자로부터 입력 받은 문자열을 적절하게 구분해서 특정 변수에 담아 필요한 함수에 인자들을 보내주는 것이다.
그것을 구현한 코드가 이 부분이며, parse 문자열 배열에 명령어들을 저장하여 꺼내 쓸 수 있다.command line으로 입력이 들어올 때 1차로 main함수에서 parsing한 후 전달하고,
우리의 목표는 run 뒤에 ' ' 안에 있는 문자를 parsing하는 것이다.
System Call
System call
Pintos에서 시스템 호출(System Call)은 사용자 프로그램이 커널 기능을 호출하는 인터페이스다.
사용자 프로그램은 시스템 호출을 사용하여 커널에게 특정 서비스를 요청하고, 커널은 해당 서비스를 수행한 후 결과를 사용자 프로그램에 반환한다.
Kernel mode
- 프로그램이 현재 CPU 상태를 저장함
- 커널이 인터럽트나 시스템 콜을 직접 처리. 즉 CPU에서 커널 코드가 실행됨
- 처리가 완료되면 중단됐던 프로그램의 CPU 상태를 복원
↓ 다시 통제권을 프로그램에게 반환
User mode
- 중단됐던 부분부터 프로그램이 이어서 실행됨
* 커널모드와 유저모드 구분하는 방법
: 모드 비트(mode bit)가 있는데, 이 비트가 1이면 보통 유저모드를, 0이면 커널모드를 가리킨다.
x86_64 Calling Convention
: 함수 호출 시 레지스터 사용 및 스택 프레임 구성과 관련된 규칙을 정의한다.
Calling Convention의 주요 특징
1. 레지스터 사용:
- %eax (또는 %rax): 함수의 반환 값이 저장되는 레지스터입니다.
- %ebx, %ecx, %edx: 함수 호출에서 범용 레지스터로 사용될 수 있습니다.
- %esi, %edi: 함수 호출에서 인자 전달에 사용될 수 있습니다.
2. 스택 프레임 구성:
- 함수 호출 시, 호출하는 함수는 스택에 인자를 push하고, 반환 주소를 스택에 push합니다.
- 호출된 함수는 스택 상에 로컬 변수 및 중간 결과를 저장하기 위해 스택 프레임을 구성합니다.
- 스택 프레임은 %ebp (또는 %rbp) 레지스터를 통해 참조됩니다.
- 호출된 함수는 스택 프레임 내에서 인자를 참조합니다.
3. 인자 전달:
- 함수 호출 시, 인자는 역순으로 스택에 push됩니다. 즉, 첫 번째 인자가 가장 나중에 스택에 push됩니다.
- 인자는 호출된 함수의 스택 프레임 내에서 고정된 위치를 차지하고, %ebp (또는 %rbp) 레지스터를 통해 접근됩니다.
4. 호출 규칙:
- 호출하는 함수는 %eax (또는 %rax) 레지스터에 반환 값을 저장합니다.
- 호출된 함수는 %ebx, %ecx, %edx, %esi, %edi 레지스터를 수정할 수 있으며, 호출하는 함수는 이러한 레지스터의 값을 보존해야 합니다.
- 스택 프레임은 %ebp (또는 %rbp) 레지스터를 통해 참조됩니다.
- 호출된 함수는 %ebp 레지스터를 이용하여 이전 스택 프레임을 복원하고, 반환 주소를 찾을 수 있습니다.
File Descriptor
File Descriptor (FD)
: 파일이나 입출력 장치에 접근하고 조작하기 위한 인터페이스를 제공하는 중요한 개념이다.
file descriptor는 파일이나 입출력 장치와 연결된 파일을 식별하는 정수값이다. 운영체제는 파일 디스크립터를 사용하여 파일을 열고 읽기, 쓰기, 닫기 등의 작업을 수행한다.
File Descriptor Table(FDT)
파일 디스크립터 테이블은 일반적으로 정수 인덱스로 접근된다.
- 0번: 표준 입력(stdin)
- 1번: 표준 출력(stdout)
- 2번: 표준 오류(stderr)
이 외의 파일 디스크립터는 사용자가 파일을 열거나 생성할 때 할당되며, 필요에 따라 추가적인 파일을 열 수 있습니다. 파일 디스크립터는 정수 값으로 표현되며, 양수인 경우 열린 파일에 대한 참조를 나타냅니다. 파일 디스크립터가 음수인 경우 주로 에러를 나타낸다.
파일 디스크립터 테이블을 통해 프로세스는 열린 파일에 대한 정보를 추적하고 필요한 작업을 수행할 수 있습니다. 파일 디스크립터 테이블은 프로세스의 컨텍스트에서 중요한 역할을 하며, 파일의 열기, 닫기, 읽기, 쓰기 등과 같은 파일 조작을 관리하는 데 사용된다.
Register
Register
: CPU가 요청을 처리하는데 필요한 데이터를 일시적으로 저장하는 기억장치
CPU는 레지스터를 사용하여 데이터를 저장, 조작 및 전달하며, 프로그램 실행 중에 중간 결과를 유지하고 제어 흐름을 조작하는 데 중요한 역할을 한다.
레지스터의 기능
: 데이터 저장, 주소 및 인덱스 저장, 연산에 사용, 프로그램 카운터 저장 등이 있다.
레지스터의 종류
1. 프로그램 카운터(Program Counter)
: 다음에 실행할 명령어의 주소를 가리키는 레지스터입니다. CPU는 프로그램 카운터의 값을 업데이트하여 다음에 실행할 명령어를 지정합니다.
2. 누산기(Accumulator)
: 산술 및 논리 연산의 중간 결과를 저장하는 데 사용되는 가장 일반적인 레지스터입니다. 연산자와 피연산자 간의 계산을 수행하고 그 결과를 누산기에 저장합니다.
3. 베이스 레지스터 (Base Register)
: 주소 계산에 사용되는 베이스 주소를 저장하는 데 사용됩니다. 주소 계산에 사용되는 기준이 되는 주소가 저장되어, 상대 주소를 절대 주소로 변환하는 데 활용됩니다.
4. 인덱스 레지스터 (Index Register)
: 주소 계산에 사용되는 인덱스 값을 저장하는 데 사용됩니다. 인덱스 레지스터와 베이스 레지스터의 조합을 통해 배열이나 구조체와 같은 자료 구조에 효율적으로 액세스할 수 있습니다.
5. 스택 포인터 (Stack Pointer): 스택의 최상단 주소를 가리키는 레지스터입니다. 스택은 함수 호출, 지역 변수 및 임시 데이터의 저장에 사용되는 중요한 데이터 구조입니다. 스택 포인터는 스택의 상태를 추적하고 스택 프레임의 생성 및 소멸을 관리하는 데 사용됩니다.
6. 프레임 포인터 (Frame Pointer): 함수 호출 시 스택 프레임의 위치를 가리키는 레지스터입니다. 프레임 포인터는 함수 내부의 지역 변수와 매개 변수에 접근하기 위해 사용됩니다.
7. 상태 레지스터 (Flags Register): 연산의 결과나 CPU의 상태 정보를 저장하는 레지스터입니다. 주로 조건 분기 명령어의 조건 검사 결과를 저장하고, 연산의 오버플로우, 제로, 부호 등의 상태를 표시합니다.
-> Pintos에서 syscall_handler()가 제어권을 얻으면 system call number는 rax에 rax에 있고 argument는 %rdi, %rsi, %rdx, %r10, %r8, %r9의 순서로 전달된다.
- EAX (Extended Accumulator): 산술 및 논리 연산의 결과를 저장하는 일반 목적의 누산기
- EBX (Extended Base Register): 주소 계산에 사용되는 베이스 주소를 저장하는 레지스터
- ECX (Extended Counter Register): 반복문에서 반복 횟수를 카운트하는 데 사용되는 레지스터
- EDX (Extended Data Register): 입출력 작업과 곱셈 또는 나눗셈 연산에 사용되는 보조 누산기
- ESP (Extended Stack Pointer): 스택의 최상단 주소를 가리키는 레지스터
- EBP (Extended Base Pointer): 함수 호출 시 스택 프레임의 위치를 가리키는 레지스터
- ESI (Extended Source Index): 메모리 블록 복사 및 문자열 연산에 사용되는 소스 인덱스 레지스터
- EDI (Extended Destination Index): 메모리 블록 복사 및 문자열 연산에 사용되는 대상 인덱스 레지스터
-> 32bit 환경에서는 E로 시작하고, 64bit 환경에서는 R로 시작한다.
Fork()
프로세스에서 fork를 실행하면 프로세스 안에 중첩된 자식 프로세스가 생성되는 것이기 때문에 상호 간의 포함관계를 설명하기 위한 구조체가 필요하다.
위 그림이 fork 된 프로세스와 부모 프로세스의 관계이며, 이에 맞게 thread 구조체에 child들을 담아놓을 수 있는 list와 그곳에 넣을 child_elem, 세마포어 등을 선언해주어야 한다.
Trouble Shooting
1. `\n`(line feed, 줄바꿈)문자
void exit(int status) { struct thread *curr = thread_current(); curr->exit_status = status; printf("%s: exit(%d)", curr->name, status); // printf("%s: exit(%d)\n", curr->name, status); thread_exit(); }
- syscall의 `exit()`함수에서 `\n`을 넣지 않으면 일어나는 에러
- 인터넷 검색으로 해결 안됨2. 주소 체크
void check_address (void *addr) { if (is_kernel_vaddr(addr)) // 1 exit(-1); if (addr == NULL) // 2 exit(-1); //! user영역이라도 할당되지 않을 수 있으니 pml4_get_page 함수를 통해서 할당 되어있는지 확인 if (pml4_get_page(thread_current()->pml4, addr) == NULL) // 3 exit(-1); }
- 처음에 주소가 할당되지 않아 NULL이거나, 커널 영역에 있으면 exit(-1)로 종료하게 진행
- 몇몇 오류가 해결되지 않아 test code확인해보니 주소값을 해당 함수(ex. create)로 보내주는데,
`check_address()`함수로 page로 할당을 받았는지 확인을 하지 않고 바로 함수를 실행하려하기에 `page_fault`가 발생
- 따라서, pml4_get_page를 통해 해당 주소가 할당받은 페이지에 있는지 확인이 필요3. file_close() 문제
'Pintos' 카테고리의 다른 글