이 문서는 카이스트 핀토스 원유집 교수님의 강의를 참고하여 작성하였습니다.
이 문서는 카이스트 핀토스 프로젝트 가이드라인을 정리한 글입니다.
Part 2 : User Program
현재 핀토스는 명령어 전체 한 줄을 통째로 읽는다.
'echo x y z'
thread name : 'echo x y z'
find program with file name : 'echo x y z'
arguments : 'echo', 'x', 'y', and 'z' are not passed
각각의 인자가 구분되도록 수정해야한다.
thread name : 'echo'
find program with file name : 'echo'
pushl the arguments(x, y, z) to user stack.
수정해야 하는 파일 : process.*
현재 PintOS의 process_create_initd함수에는 인수(arguments)들로 user stack을 초기화하는 메커니즘이 없다.
이번에 구현해야 할 숙제는 위의 메커니즘이다.
구현할 것은 다음과 같다.
1. 명령을 /bin/ls, -l, foo, bar와 같이 단어로 구분한다.
2. 단어를 스택 맨 위에 놓는다. 포인터를 통해 참조되기 때문에 순서는 중요하지 않다.
3. 오른쪽에서 왼쪽 순서로 스택에서 각 문자열과 널 포인터 센티널의 주소를 푸시한다. 이들은 argv의 요소다. null 포인터 센티널은 argv[argc]가 C 표준에서 요구하는 대로 null 포인터임을 확인한다. 이 순서는 argv[0]이 가장 낮은 가상 주소에 있도록 한다. 단어 정렬 액세스는 정렬되지 않은 액세스보다 빠르므로 최상의 성능을 위해 첫 번째 푸시 전에 스택 포인터를 8의 배수로 반올림한다. 👉 8의 배수를 만들기 위해서 아래 표에서 word-align이라는 패딩값을 넣어준다.
4. %rsi를 argv(argv[0]의 주소)로 가리키고 %rdi를 argc로 설정한다.
5. 마지막으로 가짜 "반환 주소"를 푸시한다. 진입 함수는 절대 반환되지 않지만 스택 프레임은 다른 것과 동일한 구조를 가져야 한다.
Address | Name | Data | Type |
0x4747fffc | argv[3][...] | 'bar\0' | char[4] |
0x4747fff8 | argv[2][...] | 'foo\0' | char[4] |
0x4747fff5 | argv[1][...] | '-l\0' | char[3] |
0x4747ffed | argv[0][...] | '/bin/ls\0' | char[8] |
0x4747ffe8 | word-align | 0 | uint8_t[] |
0x4747ffe0 | argv[4] | 0 | char * |
0x4747ffd8 | argv[3] | 0x4747fffc | char * |
0x4747ffd0 | argv[2] | 0x4747fff8 | char * |
0x4747ffc8 | argv[1] | 0x4747fff5 | char * |
0x4747ffc0 | argv[0] | 0x4747ffed | char * |
0x4747ffb8 | return address | 0 | void (*) () |
RDI: 4 | RSI: 0x4747ffc0
이 예에서 스택 포인터는 0x4747ffb8로 초기화된다. 위에 표시된 대로 코드는 include/threads/vaddr.h에 정의된 USER_STACK에서 스택을 시작해야 한다.
인수 전달 코드를 디버깅하는 데 유용한 <stdio.h>에 선언된 비표준 hex_dump() 함수를 찾을 수 있다.
원하는 방식으로 인수 문자열을 구문 분석할 수 있다. 구문 분석에 대한 힌트는 include/lib/string.h에서 프로토타입이 생성되고 lib/string.c에서 철저한 주석으로 구현된 strtok_r()을 확인하면 된다.
이전 글의 load 함수 주석에서 얘기한 *rsp와 strtok_r()을 활용하여 이번 프로젝트 첫 번째 챌린지를 완성하자.
2022.12.23 - [TIL (Today I Learned)/컴퓨터 시스템(CS)] - [CS] Kaist PintOS THREAD, User Programs #3
[CS] Kaist PintOS User Programs, 프로그램 구조 설명 #3
이 문서는 카이스트 핀토스 원유집 교수님의 강의를 참고하여 작성하였습니다. 이 문서는 카이스트 핀토스 프로젝트 가이드라인을 정리한 글입니다. Part 2 : User Program 1. 핀토스(다른 운영체제가
roll-over-program.tistory.com
#1 strtok_r (for passing the arguments)
char *
strtok_r (char *s, const char *delimiters, char **save_ptr) {
char *token;
ASSERT (delimiters != NULL);
ASSERT (save_ptr != NULL);
/* If S is nonnull, start from it.
If S is null, start from saved position. */
if (s == NULL)
s = *save_ptr;
ASSERT (s != NULL);
/* Skip any DELIMITERS at our current position. */
while (strchr (delimiters, *s) != NULL) {
/* strchr() will always return nonnull if we're searching
for a null byte, because every string contains a null
byte (at the end). */
if (*s == '\0') {
*save_ptr = s;
return NULL;
}
s++;
}
/* Skip any non-DELIMITERS up to the end of the string. */
token = s;
while (strchr (delimiters, *s) == NULL)
s++;
if (*s != '\0') {
*s = '\0';
*save_ptr = s + 1;
} else
*save_ptr = s;
return token;
}
주석 해석
- 문자열을 DELIMITERS(구분 문자)로 구분된 토큰으로 나눈다. 이 함수가 처음 호출될 때 S는 토큰화할 문자열이어야 하며 후속 호출에서는 널 포인터여야 한다. SAVE_PTR은 토크나이저의 위치를 추적하는 데 사용되는 'char *' 변수의 주소다. 매번 반환 값은 문자열의 다음 토큰이거나 토큰이 남아 있지 않으면 null 포인터다.
- 이 함수는 인접한 여러 구분 기호를 단일 구분 기호로 취급합니다. 반환된 토큰은 길이가 0이 되지 않습니다.
DELIMITERS는 단일 문자열 내에서 한 호출에서 다음 호출로 바뀔 수 있습니다.
- strtok_r()은 구분 기호를 null 바이트로 변경하여 문자열 S를 수정한다. 따라서 S는 수정 가능한 문자열이어야 한다. 특히 문자열 리터럴은 이전 버전과의 호환성을 위해 'const'가 아니지만 C에서 수정할 수 *없다*.
사용 예
char s[] = " String to tokenize. ";
char *token, *save_ptr;
for (token = strtok_r (s, " ", &save_ptr); token != NULL; token = strtok_r (NULL, " ", &save_ptr))
printf ("'%s'\n", token);
/*
outputs:
'String'
'to'
'tokenize.'
*/
#2 hex_dump (for check)
void
hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii) {
const uint8_t *buf = buf_;
const size_t per_line = 16; /* Maximum bytes per line. */
while (size > 0) {
size_t start, end, n;
size_t i;
/* Number of bytes on this line. */
start = ofs % per_line;
end = per_line;
if (end - start > size)
end = start + size;
n = end - start;
/* Print line. */
printf ("%016llx ", (uintmax_t) ROUND_DOWN (ofs, per_line));
for (i = 0; i < start; i++)
printf (" ");
for (; i < end; i++)
printf ("%02hhx%c",
buf[i - start], i == per_line / 2 - 1? '-' : ' ');
if (ascii) {
for (; i < per_line; i++)
printf (" ");
printf ("|");
for (i = 0; i < start; i++)
printf (" ");
for (; i < end; i++)
printf ("%c",
isprint (buf[i - start]) ? buf[i - start] : '.');
for (; i < per_line; i++)
printf (" ");
printf ("|");
}
printf ("\n");
ofs += n;
buf += n;
size -= n;
}
}
주석 해석
- BUF의 SIZE 바이트를 줄당 16개로 정렬된 16진수 바이트로 콘솔에 덤프한다. BUF의 첫 번째 바이트에 대해 OFS에서 시작하는 숫자 오프셋도 포함된다. ASCII가 참이면 해당 ASCII 문자도 함께 렌더링된다.
사용 방법
process_exec함수에서 load에 성공하면 호출
'TIL (Today I Learned) > 컴퓨터 시스템(CS)' 카테고리의 다른 글
[pintOS 3] Thierry Snas의 Virtual Memory 개요 (2) | 2023.01.03 |
---|---|
[CS] Kaist PintOS User Programs, System Call #5 (1) | 2022.12.27 |
[CS] Kaist PintOS User Programs, 프로그램 구조 설명 #3 (0) | 2022.12.23 |
[CS] Kaist PintOS THREAD, Priority Scheduling #2 (2) | 2022.12.19 |
[CS] Kaist PintOS THREAD, Alarm clock #1 (0) | 2022.12.17 |
댓글