8:58 입실
핵심 원리: 인접한 두 요소를 비교할 때 더 큰 요소를 뒤로 스왑. 끝까지 가면 마지막 요소는 정렬이 된다. 그 다음은 n - 1개의 요소에 대하여 같은 작업을 반복한다.
다음주 (목) 1회 발표
Argument Passing
User Memory
두 항목은 테스트 패스 어렵다.
실제 출력 결과를 보고 통과 여부 체크해라.
extra 케이스는 옵션
아토믹 오퍼레이션을 보장하기 위한 장치가 무엇이 있는지 찾아보기
rax 레지스터 유래 찾아보기
원래 a, b, c였음
거기에 앞/뒤에 e, r, x를 붙이는 형식
이 레지스터의 사용 용도
운영체제 실행 수준
운영체제는 프로그램 실행 시 두 가지 상태를 지닌다.
운영체제 보안과 안정성을 유지하기 위한 목적이다.
CPU에서 실행 중인 코드의 권한 수준
코드 세그먼트 셀렉터 내에 저장된다.
코드 세그먼트 셀렉터는 현재 실행 중인 코드가 저장된 메모리 세그먼트 식별 정보
코드 세그먼트 레지스터(Code Segment Register, CS) 내에서 관리
x86기준 코드 세그먼트 레지스터의 하위 2비트로 CPL을 나타낸다.
메모리 세그먼트 디스크립터 내에 설정된 권한 수준
이 세그먼트(코드, 데이터)에 접근하기 위한 최소 권한 수준
프로세스(스레드)의 CPL이 DPL보다 낮거나 같아야 이 세그먼트 접근 가능
시스템 자체가 단일적인 '커널 모드', '유저 모드'로 변환되는 게 아니다. 각각의 실행 컨텍스트가 자신의 권한 레벨에 따라 동작하는 것이다.
커널은 핵심, 핵, 알갱이 라는 뜻
CPU 상태 레지스터 내에 위치
현재 실행중인 프로세스의 권한에 따라 커널, 유저 모드가 비트로 기록된다.
프로세스는 시스템 콜 등을 통해 커널 모드로 전환할 수 있음.
이런 전환은 해당 프로레스(스레드)에만 적용되는 것
(11/25 학습 내용)
응용 프로그램은 시스템 콜을 호출하고,
커널이 호출된 시스템 콜을 실행한다.(특권 명령어 실행)
제한적 직접 실행(Limited Direct Execution)
CPU 가상화가 제한적 직접 실행임!
문맥 교환 등을 통해 cpu가 여러 개인 것처럼 쓸 수 있는데 이때, 커널와 유저를 나누어서 제한적으로 직접 실행한다.
프로세스를 CPU에서 제한 없이 직접 실행하면 하드웨어 접근 등 모든 명령어를 제한 없이 실행할 수 있음.
그래서 trap table을 만들고 이를 통해 시스템을 통제한다. 즉, 프로세스는 CPU에서 직접적으로 실행되기는 하지만 일부 제한을 받는다.
시스템 콜이 발생하면 trap table을 통해 어떤 핸들러(메모리 위치)를 실행해야 할지 알려준다.
너무 궁금했던 게 책에 있었다!!
시스템 콜은 왜 일반적인 프로시저 콜처럼 생겼을까?
사실 시스템 콜도 평범한 C함수임.
대신 시스템 콜 부분은 어셈블리어로 작성되어 있음.
하지만 이 시스템 콜은 내부에서 trap을 호출한다.
시스템 콜이 호출되면 시스템 콜은 인자와, open()에 해당하는 시스템 콜을 커널과 약속된 메모리 공간이나 레지스터에 저장. (모든 시스템 콜은 고유 번호가 있음)
그리고 trap 명령어를 실행.
(trap은 하드웨어에서 발생하는 이벤트)
이때 제어권이 커널로 넘어감.
trap이 실행되고 특권 명령어가 실행되고 난 후 c라이브러리가 시스템 콜의 리턴값을 읽고, 제어권을 시스템 콜을 호출한 프로그램에게 다시 넘긴다.
트랩 테이블은 커널이 부팅 시에 만든다.
그럼 사용자가 직접 시스템 콜을 실행시키면 되는거 아닌가?
시스템 콜의 코드 위치는 운영체제만 알고 있고 응용 프로그램은 모른다.
하드웨어에게 트랩 테이블의 위치를 알려주는 건 매우 강력한 기능! 이것 또한 특권 명령어임.
시스템 콜을 공부하면서 느낀 점. 웹의 프론트와 백엔드와 비슷하다.
유저 영역을 프론트엔드, 크널 영역을 백엔드(데이터베이스)로 본다면 비슷하다.
즉, API요청(시스템 콜)을 통해 제한적으로 백엔드와 데이터베이스에 접근시키고 나머지는 유저 모드에서 동작해야 한다!
그래서 사용자의 입력값을 주의해야 한다. 시스템 콜도 인자로 넘어온 값으로 커널 영역에 메모리에 접근한다 거나 문제를 막아야 하는 것처럼, 웹도 sql 인젝션과 같은 기법 등으로 데이터베이스 등에 접근하게 되는 문제나, 백엔드의 관리자 권한을 탈취당하는 일 등을 방지해야 한다!
커널은 user stack에 접근할 수 없다.
커널 스택은 각 프로세스마다 생성된다.
각 프로세스는 고유한 커널 스택을 갖고 있다.
커널 코드는 모든 프로세스가 공유하지만, 커널 스택은 각 프로세스마다 격리되어 있다.
여러 작업이 동시에 실행되는 다중 프로세스 환경에서 데이터 일관성과 안정성을 보장하기 위한 개념
여러 개의 연산 묶음이 마치 하나의 연산인 것처럼 0아니면 1로 실행되어야 함.
아토믹 오퍼레이션을 위한 여러 개념들 찾아보기
하드웨어와 소프트웨어 아키텍처 차이
-- | 32bit OS | 64bit OS |
---|---|---|
주소 공간 | 최대 약 4GB | 테라, 페타 수준 |
레지스터 크기 | 32bit | 64bit |
CPU 호환 | 32bit CPU만 사용 | 32/64bit CPU 호환 |
https://velog.io/@suseodd/Ch3.4-Accessing-Information
반드시 목적대로 사용해야 하는 건 아니지만 가급적 목적대로 사용하는데 코드 가독성이 좋아진다.
8비트에서는 A(C), B, C, D 같은 형태.
16비트에서는 AX와 같은 형태.
32비트가 되면서 Extended가 붙어서 EAX.
64비트가 되면서 Register가 붙어서 RAX.
출처: https://5kyc1ad.tistory.com/32 [Re: 제로부터 시작하는 블로그 생활:티스토리]
캐시 메모리
파일 또는 I/O 리소스 식별자
STDIN: 0 (키보드 입력)
STDOUT: 1 (콘솔 출력)
STDER: 2 (오류 메시지 출력)
FAIL tests/userprog/args-none
FAIL tests/userprog/args-single
FAIL tests/userprog/args-multiple
FAIL tests/userprog/args-many
FAIL tests/userprog/args-dbl-space
FAIL tests/userprog/halt
FAIL tests/userprog/exit
FAIL tests/userprog/create-normal
FAIL tests/userprog/create-empty
FAIL tests/userprog/create-null
FAIL tests/userprog/create-bad-ptr
FAIL tests/userprog/create-long
FAIL tests/userprog/create-exists
FAIL tests/userprog/create-bound
FAIL tests/userprog/open-normal
FAIL tests/userprog/open-missing
FAIL tests/userprog/open-boundary
FAIL tests/userprog/open-empty
FAIL tests/userprog/open-null
FAIL tests/userprog/open-bad-ptr
FAIL tests/userprog/open-twice
FAIL tests/userprog/close-normal
FAIL tests/userprog/close-twice
FAIL tests/userprog/close-bad-fd
FAIL tests/userprog/read-normal
FAIL tests/userprog/read-bad-ptr
FAIL tests/userprog/read-boundary
FAIL tests/userprog/read-zero
FAIL tests/userprog/read-stdout
FAIL tests/userprog/read-bad-fd
FAIL tests/userprog/write-normal
FAIL tests/userprog/write-bad-ptr
FAIL tests/userprog/write-boundary
FAIL tests/userprog/write-zero
FAIL tests/userprog/write-stdin
FAIL tests/userprog/write-bad-fd
FAIL tests/userprog/fork-once
FAIL tests/userprog/fork-multiple
FAIL tests/userprog/fork-recursive
FAIL tests/userprog/fork-read
FAIL tests/userprog/fork-close
FAIL tests/userprog/fork-boundary
FAIL tests/userprog/exec-once
FAIL tests/userprog/exec-arg
FAIL tests/userprog/exec-boundary
FAIL tests/userprog/exec-missing
FAIL tests/userprog/exec-bad-ptr
FAIL tests/userprog/exec-read
FAIL tests/userprog/wait-simple
FAIL tests/userprog/wait-twice
FAIL tests/userprog/wait-killed
FAIL tests/userprog/wait-bad-pid
FAIL tests/userprog/multi-recurse
FAIL tests/userprog/multi-child-fd
FAIL tests/userprog/rox-simple
FAIL tests/userprog/rox-child
FAIL tests/userprog/rox-multichild
FAIL tests/userprog/bad-read
FAIL tests/userprog/bad-write
FAIL tests/userprog/bad-read2
FAIL tests/userprog/bad-write2
FAIL tests/userprog/bad-jump
FAIL tests/userprog/bad-jump2
자.. 해결해보자..
테스트 케이스를 보면 이번 과제는 함수를 호출하고, 파일을 CRUD하는 과정을 구현하는 건가..?
즉, 함수 호출과 파일 입출력 구현하는 것?
프로세스 초기화, 생성, 포크 등 프로세스를 제어하는 것 같음
ELF 바이너리를 실행한다고 함.
ELF: Executable and Linkable Format(유닉스 계열에서 실행 가능한 파일 포맷)
시스템 호출 처리기
프로젝트2에서 구현해야 할 코드!
유저 프로그램이 특권 명령어나, 금지된 명령어를 실행할 때 exception이나 fault로 트랩을 발생시키는 것!
프로젝트2에서는 page_fault()
를 구현해야 함!
직접 관련은 없지만 간단한 파일 시스템이니까 흥미 차원에서 살펴보기!
출처: https://dlforbi.tistory.com/16
thread/mmu.c
, include/threads/vaddr.h
살펴보기
비정상적인 포인터는 page fault를 유발한다. userprog/exception.c에 있는 page_fault()를 수정해서 핸들링.
방법2가 mmu(Memory Management Unit)를 활용해서 일반적으로 더 빠르다. 그리고 리눅스 등의 실제 커널에서 사용하는 방법임.
process.c
precess_exec()
인수 전달을 구현합니다.
현재는 process_exec()새 프로세스에 인수 전달을 지원하지 않습니다. process_exec()단순히 프로그램 파일 이름을 인수로 사용하는 대신 공백에서 단어로 나누도록 확장하여 이 기능을 구현합니다 . 첫 번째 단어는 프로그램 이름이고 두 번째 단어는 첫 번째 인수 등입니다. 즉, process_exec("grep foo bar")두 개의 인수 foo와 bar를 전달하여 grep을 실행해야 합니다.
들어오는 값을 적당히 파싱해서 프로그램명과 인수명으로 분할해야 하는 것 같음.
분할은 알아서 하면 되는데 길을 잃으면(?) strtok_r()을 참고하라고 함.
/* The following uses strtok_r() to parse two strings using separate contexts: */
char test[80], blah[80];
char *sep = "\\/:;=-";
char *word, *phrase, *brkt, *brkb;
strcpy(test, "This;is.a:test:of=the/string\\tokenizer-function.");
for (word = strtok_r(test, sep, &brkt);
word;
word = strtok_r(NULL, sep, &brkt))
{
strcpy(blah, "blah:blat:blab:blag");
for (phrase = strtok_r(blah, sep, &brkb);
phrase;
phrase = strtok_r(NULL, sep, &brkb))
{
printf("So far we're at %s:%s\n", word, phrase);
}
}
이번 과제 정리
1. 프로그램 실행 코드 구현
2. 프로세스 관련 코드 구현
3. 시스템콜 관련 코드 구현
4. 번외