process_exec() 함수 수정
호출 규약 내에서 함수 인자와 반환 값이 어떻게 전달되는지 알아본다.
🤔 parsing을 진행하는 이유?
thread_create 시 스레드 이름을 실행 파일과 동일하게 만들어 주기 위해 parsing 진행
key point
/bin/ls -l foo bar
이러한 command가 주어졌을 때 명령을 단어로 나누어서 스택의 맨 위에 단어를 배치한다.
깃북 정리
현재 process_exec()은 새 프로세스에 인수를 전달하는 기능을 지원하지 않음 따라서 process_exec()를 확장하여 단순히 프로그램 파일 인자를 인자로 받는 대신 공백으로 단어를 나누어야 함
-> 이때 핀토스 유틸리티가 커널에 전달 할 수 있는 명령줄 인수는 128바이트로 제한되어 있음
리눅스 명령어 정리
📂 memcpy : source 메모리의 재용을 바로 destination에 복사
📂 memmove : source 메모리의 내용을 임시 공간에 저장한후 destination에 복사
-> 속도는 memcpy가 더 빠르지만 안전하게 동작하는 것은 memmove이다.
📂 memcmp : buf1과 buf2의 첫번쨰 count 바이트를 비교
📂 strcmp : 두개의 문자열을 비교하여 문자열이 완전히 같다면 0을 반환, 다르면 음수 혹은 양수를 반환
📂 memchr : block부터 시작되는 첫번째 size 바이트에서 첫번째 발생 포인터를 반환
📂 strchr : 문자열에서 처음 나오는 부분을 찾아서 반환하거나 c가 나타나지 않으면 널포인터를 반환
📂 strcspn : 문자열의 초기 부분 문자열의 길이를 반환
📂 strpbrk : 문자열의 첫번째 문자에 대한 포인터를 반환
📂 strrchr : 문자열에서 마지막으로 발생한 곳에 대한 포인터를 반환
📂 strspn : skip의 문자로 구성된 문자열의 초기 부분 문자열의 길이를 반환
📂 strstr : 첫번째 발생에 대한 포인터를 반환하고, 만약 존재하지 않는다면 null 포인터를 반환
📂 strtok_r : 문자열 S를 수정하여 구분자를 null로 변경, 따라서 s는 수정 가능한 문자열이여야한다.
📂 memset : DST의 사이즈(바이트)를 VALUE값으로 설정
📂 strlen : 문자열의 길이를 반환
📂 strnlen : 문자열의 길이가 maxlen 문자보다 작으면 실제길이를 반환, 그렇지 않으면 maxlen을 반환
📂 strlcpy : 안전한 문자열 복사
📂 strlcat : 안전한 문자열 연결
번외- 같은 기능이지만 안전하지 않음, strlcpy
,strlcat
은 모두 null 종료를 보장하고, 문자열의 크기(바이트)를 길이 매개변수로 받으며, 잘림을 쉽게 감지할 수 있는 방법을 제공한다. 또한 대상에서 사용되지 않은 바이트를 0으로 채우지 않는다.
strncpy() : 문자열 복사
strncat() : 문자열 연결
main()함수의 인자 , 사용자 프로그램을 실행할 때 넣어주는 인자에 대한 정보
*argv[] = **argv
)위 그림에서 스택 포인터(rsp)는 0x4747ffb8로 초기화 된다. 이때 return address가 실행되면 os가 종료된다.
%rsi
는 argv[0]으로 설정되고, %rdi
(인자의 개수)는 argc로 4개로 설정된다. (argv[0]~argv[3])
1번은 문자열을 나타내고 2번은 문자열(argument)의 주소를 나타낸다.
: ELF 파일을 HEX 형태로 출력한 것!
- pintos-mkdisk filesys.dsk 10
: 이 명령어를 입력하면 , 10MB 크기의 filesys.dsk 가상 디스크가 생성된다.
- pintos --fs-disk filesys.dsk 10 -p tests/userprog/args-single:args-single -- -q -f run 'args-single onearg"
: 먼저 --fs-disk 옵션을 통해 가상 디스크를 사용한다는 것을 알려주고, 뒤에는 사용하고 있는 가상 디스크의 경로를 입력한다. -p tests/userprog/args-single:args-single
는 가상 디스크가 비어있으면, 파일을 실행할 수 없기에 테스트에서 이용할 파일을 가상 디스크에 복사하는 작업을 하는 코드이다. 이 코드는 [파일의 경로]:[가상디스크에 복사할 이름]을 의미한다.
pintos --fs-disk=10 -p tests/userprog/args-single:args-single -- -q -f run 'args-single onearg'
: 이번 테스트에서만 10MB 크기의 가상 디스크를 임시로 만들어 활용할 수 있다. 이렇게 하면 pintos-mkdisk 명령어를 통해 가상 디스크를 만들어주지 않아도 테스트 진행이 가능하다.
가상 메모리 관리 시스템은 페이지 테이블이라는 데이터 구조를 사용하여 가상 주소를 물리적인 메모리 주소로 변환합니다. 이러한 페이지 테이블은 계층적 구조로 되어 있으며, PML4는 그 중 최상위 계층의 페이지 테이블을 의미합니다.
PML4는 512개의 64비트 엔트리로 구성되며, 각 엔트리는 페이지 디렉터리 포인터 테이블 (PDPT)의 시작 주소를 나타냅니다. PDPT는 다음 계층의 페이지 디렉터리 테이블 (PDT)의 시작 주소를 가리키며, PDT는 페이지 테이블 (PT)의 시작 주소를 가리킵니다. PT는 실제 페이지 프레임의 주소를 가지고 있습니다.
PML4는 가상 주소 공간의 최상위 계층을 관리하며, 페이지 테이블 계층의 모든 계층을 연결하는 역할을 수행합니다. PML4 엔트리는 512GB 크기의 가상 주소 공간을 나타내며, 이를 통해 운영체제와 응용 프로그램은 물리 메모리를 효율적으로 관리하고 사용할 수 있습니다.
각 시스템 호출에 대한 시스템 호출 번호는 include/lib/syscall-nr.h
에 정의되어 있습니다:
바이트 저장 순서
->컴퓨터가 데이터를 메모리에 저장할 때 byte 단위로 나눠서 저장한다. 이때 바이트가 저장된 순서에 따라 빅 엔디안, 리틀 엔디안 2가지 방식으로 나뉜다.
예를 들어 "0x12345678"을 저장한다고 하면
예를 들어 "0x12345678"을 저장한다고 하면
우리가 현재 사용하고 있는 x86 아키텍처가 리틀 엔디안을 사용하기 때문에 대부분의 데스크톱 컴퓨터는 리틀 엔디언 방식을 사용한다.
빅 엔디안은 스프트웨어의 디버그를 편하게 해주는 경향이 있다.
caller
호출 하는 함수 , main() 함수에서 func()이 호출되는 경우 main()
callee
호출 되는 함수, main() 함수에서 func()이 호출되는 경우 func()
콜러가 저장하는 레지스터 ( caller-saved-register)
: 사용하기 전 콜러가 반드시 백업해야 하는 레지스터, 콜리는 사용 가능
: 값이 보존되지 않아도 되는 경우에는 백업하지 않을 수 있다.
콜러가 저장하는 레지스터 ( callee- saved -register)
: 사용하기 전 콜리가 반드시 백업해야 하는 레지스터, 콜러는 사용 가능
: 함수 복귀 전 다시 값을 복원
: 콜리 입장에서는 콜러가 백업을 필요로 하는 레지스터가 무엇인지 모르므로 무조건 백업해야 함
운영체제의 커널이 프로세스들을 구분하기 위해 사용하는 프로세스 식별자(이름)
Memory
메모리는 컴퓨터 프로그램, 명령어, 데이터를 저장하는 데 사용되는 하드웨어 장치
메인메모리(RAM)이 있고, 보조 메모리(하드 디스크 드라이브)가 있음
(1) RAM
Register
프로세서가 요청을 처리하는 데 필요한 데이터를 일시적으로 저장하는, 프로세서에 위치한 아주 빠른 기억장치
이 때 레지스터의 용량(비트 수)는 CPU의 속도를 의미한다. 64비트 레지스터가 32비트 레지스터보다 빠르다.
한 프로세스가 운영체제의 사용자 모드에서 실행되는 경우, 해당 프로세스에서 호출한 함수와 인자들은 그 프로세스의 유저 스택에 저장
%register
: 레지스터가 저장한 값
(%register)
: 레지스터가 저장한 주소가 가리키는 값
특정 아키텍처와 운영체제의 기능들이 상호 작용하는 방식에 대한 규약
함수 인수가 어떻게 배치되는지, 반환 값이 어디로 가는지, 레지스터 함수가 사용할 수 있는 것, 로컬 변수를 할당하는 방법이 포함 됨
%rdi
, %rsi
,%rdx
,%rcx
,%r8
, %r9
레지스터에 전달된다. 7번쨰 인자부터는 스택에 전달한다.return address
)를 스택 맨 처음에 저장한 후 Callee 첫 명령으로 점프한다.(CALL
)%rax
레지스터에 저장한다.리눅스에서 실행 가능(EXCUTABLE)하고 링킹 가능(Linkable)한 표준 파일 형식, 실행 파일, 목적 파일, 공유 라이브러리, 코어 파일들을 정의할 때 ELF 파일 형식으로 정의
ELF 파일 헤더
: 파일의 구성을 나타냄
섹션
: 링킹을 위한 오브젝트 파일의 특정 정보를 포함하고 있는 ELF file의 작은 조각
프로그램 헤더 테이블
: ELF 내의 세그먼트들에 대한 정보와 이들을 어떻게 메모리에 로드해야 하는지에 대한 정보를 담음
섹션 헤더 테이블
: 파일의 섹션들에 대해 알려준다.
링킹
: 오브젝트의 각 섹션들을 공유 라이브러리나 실행 파일 내에 각 세그먼트내에 정렬해주는 과정
Reference
프로세서의 구성
gitbook_kaist
ELF
user_stack에 저장되는 과정 상세히 설명한 블로그
빅 엔디안 vs 리틀 엔디안
콜러와 콜리