운영체제 project를 시작하기 전, 교안에 있는 실습 자료로 sceduling 내용에 관한 practice를 진행했다.
practice 01
- In xv6, yield is implemented but it is not the system call.
- First, implement yield system call so that user can call yield. (yield gives up its CPU)
- Make a user program in xv6 that created child process with fork(), and show that parent and child process print "Parent" and "Child" respectively in loop.
문제에서 언급된 대로, yield 함수는 이미 구현이 되어있기 때문에 따로 구현하지 않아도 된다. 구현되어 있는 이 함수를 system call에 추가해주기만 하면 된다.
/** proc.c **/
// Give up the CPU for one scheduling round.
void
yield(void)
{
struct proc *p = myproc();
acquire(&p->lock);
p->state = RUNNABLE;
sched();
release(&p->lock);
}
1. wrapper function 구현
우선 구현되어 있는 yield 함수의 wrapper function을 만들어준다. wrapper function이기 때문에 추가적인 기능 없이 그냥 yield 함수를 호출해 주면 된다.
다만, 주의할 점은 return type은 꼭 uint64로 해야 한다는 점이다. yield 함수는 return type이 void이다. 즉, 아무것도 리턴할 필요가 없다. 하지만 xv6의 system call table에 등록되는 모든 함수는 반드시 uint64를 반환하도록 정해져 있다. 따라서 나는 그냥 0을 리턴해주고 return type을 uint64로 설정해 주었다.
/** [add code] kernel/sysproc.c **/
uint64
sys_yield(void)
{
yield();
return 0;
}
2. 새로운 system call인 yield를 register
/** [add code] kernel/syscall.h **/
#define SYS_yield 22
/** [add code] kernel/syscall.c **/
extern uint64 sys_yield(void); //add
static uint64 (*syscalls[])(void) = {
//생략
[SYS_yield] sys_yield, //add
};
3. user.h에 함수 선언
/** [add code] user/user.h **/
void yield(void);
4. usys.pl에 macro 추가
/** [add code] user/usys.pl **/
entry("yield");
5. user directory에 user function 구현
마지막으로 user function을 만들어줘서, system call로 등록된 yield가 잘 동작하는지 확인해 준다. fork()로 자식 프로세스를 만들어 서로에게 cpu를 넘겨주며 실행하게 된다.
다만 여기서 주의할 점은 확인을 위해 "Parent"와 "Child"를 콘솔에 출력할 때, printf가 아닌 write를 써야 한다는 점이다. printf는 비동기적으로 실행되기 때문에 출력이 섞이거나 깨지는 현상 발생할 수 있다.
/** [new file] user/yield.c **/
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void print(const char *msg) {
write(1, msg, strlen(msg)); // write()를 사용하여 출력
}
int main(int argc, char *argv[])
{
int pid = fork();
if (pid < 0) {
print("Fork failed!\n");
exit(1);
} else if (pid == 0) {
for (int i = 0; i < 10; i++) {
print("Child\n");
yield();
}
} else {
for (int i = 0; i < 10; i++) {
print("Parent\n");
yield();
}
}
}
7. user function의 code 파일 Makefile에 추가
/** Makefile **/
UPROGS=\
// 생략
$U/_yield\
8. 결과: Children과 Parent가 번갈아가면서 실행됨.
practice 02
- One basic way to debug is to simpl print the variables, and check if they have intended values.
- Print current value of ticks variable, and the pid and the name of the process to the console, whenever the sceduler picks a process to run. (Use printf function)
- Make a user program that runs an infinite loop and execute it. (The same process should be picked on ever timer interrupt)
1. scheduler function에 현재 상태 print하는 코드 추가
/** [add code] kernel/proc.c **/
void
scheduler(void)
{
struct proc *p;
struct cpu *c = mycpu();
c->proc = 0;
for(;;){
// 생략
int found = 0;
for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state == RUNNABLE) {
printf("ticks = %u, pid = %u, name = %s\n", ticks, p->pid, p->name); //add
// 생략
}
// 생략
}
2. test를 위해 user function 만들기
현재 실행 중인 process의 상태를 print하는 것이므로, 그냥 while문을 통해 해당 프로세스가 무한히 돌아가도록 해주면 된다.
/** [new file] ueser/test_print.c **/
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
while(1) {}
return 0;
}
3. makefile에 user function code 파일 추가
/** Makefile **/
UPROGS=\
// 생략
$U/_test_printf\
4. 결과
test_printf 프로세스가 사용자에 의해 terminate 될 때까지 무한히 돌아가는 것을 볼 수 있다.

'학교 공부 > 운영체제' 카테고리의 다른 글
[운영체제 실습] thread 이용해보기 (0) | 2025.04.12 |
---|---|
[운영체제 실습] System Call 추가하기 (0) | 2025.03.21 |