해당 포스트는 MIT의 교육용 운영체제 XV6를 활용하며, 코드 저작권은 MIT에 있음을 밝힙니다.
목표
System call 함수 getnice(), setnice(), ps() 구현
과정
1. usys.S 에 syscall 추가.
...
SYSCALL(getnice)
SYSCALL(setnice)
SYSCALL(ps)
구현할 3가지 함수를 SYSCALL() 형태로 작성해 준다.
2. syscall.h에 syscall number 추가
...
#define SYS_getnice 22
#define SYS_setnice 23
#define SYS_ps 24
syscall number는 중복되지 않도록 순차적으로 준다.
3. syscall.c에서 extern 및 syscall element 추가
...
extern int sys_getnice(void);
extern int sys_setnice(void);
extern int sys_ps(void);
...
static int (*syscalls[])(void) = {
...
[SYS_getnice] sys_getnice,
[SYS_setnice] sys_setnice,
[SYS_ps] sys_ps,
};
extern 및 element를 추가해 준다.
4. sysproc.c에 sys_function 추가
...
int
sys_getnice(void)
{
int pid;
if(argint(0, &pid) < 0)
return -1;
return getnice(pid);
}
int
sys_setnice(void)
{
int pid;
int value;
if(argint(0, &pid) < 0)
return -1;
if(argint(1, &value) < 0)
return -1;
return setnice(pid, value);
}
int
sys_ps(void)
{
int pid;
if(argint(0, &pid) < 0)
return -1;
ps(pid);
return 0;
}
3가지 함수마다 각 sys_function을 추가해 준다. sys_getpname() 함수를 참고하여 각 함수에 argint()를 삽입하였다. sys_setnice()의 경우 받는 인자가 2개이므로 둘 다 argint() 판단 과정을 적용해 주었다. sys_ps()의 경우, 다른 void 형 함수인 eixt() 함수를 참고하여 ps() 함수를 호출하고 0을 리턴 하도록 작성하였다.
5. nice 값 추가하기
5.1 proc.h 에서 struct proc 에 nice 추가하기
struct proc {
uint sz; // Size of process memory (bytes)
...
int nice;
};
기존에는 없던 새로운 변수 nice를 사용하므로 이를 proc.h의 struct에 추가하였다.
5.2 proc.c 에서 static struct proc* allocproc(void) 에 nice 추가하기
...
found:
p->state = EMBRYO;
p->pid = nextpid++;
p->nice = 20;
proc.c 에서 새로운 프로세스를 할당해 주는 부분에서 nice = 20 코드를 넣어주었다.
6. proc.c에 실제로 작동하는 함수 추가하기
6.1 getnice 함수
int
getnice(int pid)
{
struct proc *p;
acquire(&ptable.lock);
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->pid == pid){
release(&ptable.lock);
return p->nice;
}
}
release(&ptable.lock);
return -1;
}
getnice() 함수는 인자로 받은 pid에 대하여 해당 프로세스의 p->nice 값을 리턴 한다. 만약 해당하는 pid가 없을경우 -1을 리턴 한다. 이때, acquire(&ptable.lock) 과 release(&ptable.lock) 을 통해 process table을 보호해준다.
6.2 setnice 함수
int
setnice(int pid, int value)
{
struct proc *p;
acquire(&ptable.lock);
if ((value<0) || (value>=40)) {
release(&ptable.lock);
return -1;
}
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->pid == pid){
p->nice = value;
release(&ptable.lock);
return 0;
}
}
release(&ptable.lock);
return -1;
}
setnice() 함수는 인자로 받은 pid와 value에 대하여 해당 프로세스의 p->nice 값을 수정한다. 만약 해당하는 pid가 없거나 value가 범위를 벗어나는 경우 (value<0) || (value>=40), -1을 리턴 한다. 이전과 동일하게, acquire(&ptable.lock) 과 release(&ptable.lock) 을 통해 process table을 보호해준다.
6.3 ps 함수
void
ps(int pid)
{
static char* states[] = {
"UNUSED ",
"EMBRYO ",
"SLEEPING",
"RUNNABLE",
"RUNNING ",
"ZOMBIE "
};
struct proc *p;
acquire(&ptable.lock);
int valid_pid = 0;
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)
if(p->pid == pid) valid_pid = 1;
if (pid == 0) valid_pid = 1;
if (valid_pid==0) {
release(&ptable.lock);
return;
}
cprintf("name\tpid\tstate\t\tpriority\n");
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if((pid == 0) || (p->pid == pid)){
if (p->state != 0)
cprintf("%s\t%d\t%s\t%d\n", p->name, p->pid, states[p->state], p->nice);
}
}
release(&ptable.lock);
return;
}
ps() 함수는 인자로 받은 pid에 대하여 해당 프로세스의 정보를 출력한다. pid가 0일 경우는 모든 프로세스를 출력한다. valid_pid 라는 값을 통해 만약 pid 가 0 이 아니며, 존재하는 pid도 아닐 경우 아무것도 출력하지 않고 종료한다. 출력 반복문에서는 pid가 0 이거나 pid에 해당하는 프로세스가 존재하면 프로세스의 정보를 출력한다. 단, p->state = 0, 즉UNUSED인 경우에는 출력하지 않는다. states[] 는 state 출력을 용이하게 하기위해 만든 배열로, proc.h의 enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; 를 참고하였다. 이전과 동일하게, acquire(&ptable.lock) 과 release(&ptable.lock) 을 통해 process table을 보호해준다.
7. defs.h 에 정의 추가하기
...
int getnice(int);
int setnice(int, int);
void ps(int);
사용할 함수의 리턴 타입과 인자 타입을 정의해준다.
8. user.h 에 정의 추가하기
...
int getnice(int);
int setnice(int, int);
void ps(int);
이전과 동일하다.
9. mytest.c 등록
// Makefile
...
_wc\
_zombie\
_mytest\
$ ls
. 1 1 512
.. 1 1 512
README 2 2 2286
cat 2 3 13704
echo 2 4 12712
forktest 2 5 8152
grep 2 6 15580
init 2 7 13456
kill 2 8 12764
ln 2 9 12668
ls 2 10 14852
mkdir 2 11 12848
rm 2 12 12828
sh 2 13 23312
stressfs 2 14 13492
usertests 2 15 56428
wc 2 16 14248
zombie 2 17 12492
mytest 2 18 12696
console 3 19 0
테스트를 위해 mytest.c 파일을 만들고 실행 가능하도록 등록해준다.
10. mytest.c 로 확인해보기
#include "types.h"
#include "user.h"
#include "stat.h"
int main ()
{
printf(1, "\ncheck all ps by ps(0):\n---------------------------\n");
ps(0);
int i;
printf(1,"\ngetnice test ...\n");
for(i=-1;i<6;i++){
printf(1,"\tgetnice(%d) \treturn: %d\n", i, getnice(i));
}
printf(1,"\tgetnice(%d) \treturn: %d\n", 1000, getnice(1000));
printf(1,"\nsetnice test 1...\n");
for(i=-1;i<6;i++){
printf(1,"\tsetnice(%d,15) \treturn: %d\n", i, setnice(i, 15));
printf(1,"\tgetnice(%d) \treturn: %d\n", i, getnice(i));
}
printf(1,"\tsetnice(%d,15) \treturn: %d\n", 1000, setnice(1000, 15));
printf(1,"\tgetnice(%d) \treturn: %d\n", 1000, getnice(1000));
printf(1, "\ncheck all ps by ps(0):\n---------------------------\n");
ps(0);
printf(1,"\nsetnice test 2...\n");
printf(1,"\tsetnice(%d,%d) \treturn: %d\n", 1, -1, setnice(1, -1));
printf(1,"\tgetnice(%d) \treturn: %d\n", 1, getnice(1));
printf(1,"\tsetnice(%d,%d) \treturn: %d\n", 1, 0, setnice(1, 0));
printf(1,"\tgetnice(%d) \treturn: %d\n", 1, getnice(1));
printf(1,"\tsetnice(%d,%d) \treturn: %d\n", 2, 39, setnice(2, 39));
printf(1,"\tgetnice(%d) \treturn: %d\n", 2, getnice(2));
printf(1,"\tsetnice(%d,%d) \treturn: %d\n", 2, 40, setnice(2, 40));
printf(1,"\tgetnice(%d) \treturn: %d\n", 2, getnice(2));
printf(1, "\ncheck all ps by ps(0) %d:\n");
ps(0);
printf(1,"\nps test ...\n");
for(i=-1;i<6;i++){
printf(1,"\nps(%d)\n---------------------------\n", i);
ps(i);
}
exit();
}
$ mytest
check all ps by ps(0):
---------------------------
name pid state priority
init 1 SLEEPING 20
sh 2 SLEEPING 20
mytest 3 RUNNING 20
getnice test ...
getnice(-1) return: -1
getnice(0) return: 0
getnice(1) return: 20
getnice(2) return: 20
getnice(3) return: 20
getnice(4) return: -1
getnice(5) return: -1
getnice(1000) return: -1
setnice test 1...
setnice(-1,15) return: -1
getnice(-1) return: -1
setnice(0,15) return: 0
getnice(0) return: 15
setnice(1,15) return: 0
getnice(1) return: 15
setnice(2,15) return: 0
getnice(2) return: 15
setnice(3,15) return: 0
getnice(3) return: 15
setnice(4,15) return: -1
getnice(4) return: -1
setnice(5,15) return: -1
getnice(5) return: -1
setnice(1000,15) return: -1
getnice(1000) return: -1
check all ps by ps(0):
---------------------------
name pid state priority
init 1 SLEEPING 15
sh 2 SLEEPING 15
mytest 3 RUNNING 15
setnice test 2...
setnice(1,-1) return: -1
getnice(1) return: 15
setnice(1,0) return: 0
getnice(1) return: 0
setnice(2,39) return: 0
getnice(2) return: 39
setnice(2,40) return: -1
getnice(2) return: 39
check all ps by ps(0) 0:
name pid state priority
init 1 SLEEPING 0
sh 2 SLEEPING 39
mytest 3 RUNNING 15
ps test ...
ps(-1)
---------------------------
ps(0)
---------------------------
name pid state priority
init 1 SLEEPING 0
sh 2 SLEEPING 39
mytest 3 RUNNING 15
ps(1)
---------------------------
name pid state priority
init 1 SLEEPING 0
ps(2)
---------------------------
name pid state priority
sh 2 SLEEPING 39
ps(3)
---------------------------
name pid state priority
mytest 3 RUNNING 15
ps(4)
---------------------------
ps(5)
---------------------------
최초의 ps(0)를 확인해 보면 모든 프로세스가 잘 나타나며, nice 값도 20으로 잘 초기화된다. getnice() 의 경우 존재하는 pid 에 대해 nice 값을 잘 출력한다. pid가 올바르지 않은 경우 -1을 잘 리턴한다. setnice() 의 경우 존재하는 pid에 대해 0을 잘 리턴하며, nice 값도 성공적으로 바뀐다. pid가 올바르지 않은 경우 -1을 잘 리턴한다. 또한, value가 -1, 40 일때 처럼 올바르지 않으면 -1을 잘 리턴한다. ps() 의 경우 각 프로세스의 name, pid, state, priority를 항목별로 잘 출력한다. 존재하는 pid를 주는 경우 각 프로세스를 잘 출력하며, pid를 0으로 주는 경우에도 존재하는 모든 프로세스를 잘 출력한다. 존재하지 않는 pid를 주는 경우 아무것도 출력되지 않는다.
Source
- mit-pdos/xv6-public
https://github.com/mit-pdos/xv6-public
-
-
- 1. usys.S 에 syscall 추가.
- 2. syscall.h에 syscall number 추가
- 3. syscall.c에서 extern 및 syscall element 추가
- 4. sysproc.c에 sys_function 추가
- 5. nice 값 추가하기
- 5.1 proc.h 에서 struct proc 에 nice 추가하기
- 5.2 proc.c 에서 static struct proc* allocproc(void) 에 nice 추가하기
- 6. proc.c에 실제로 작동하는 함수 추가하기
- 6.1 getnice 함수
- 6.2 setnice 함수
- 6.3 ps 함수
- 7. defs.h 에 정의 추가하기
- 8. user.h 에 정의 추가하기
- 9. mytest.c 등록
- 10. mytest.c 로 확인해보기
-