HomePostAbout
thumbnail
XV6 운영체제 개선해 보기 <getnice(), setnice(), ps() 구현>
OS
2023.09.02.

해당 포스트는 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

Just an developer with drinks
2022 HyeonDong, Powered By Gatsby.