1 Design
이번 과제는 getppid() 시스템 콜을 xv6 운영체제에 추가하고, 해당 시스템 콜을 호출하는 사용자 프로그램 ppid를 작성하는 것이다.
getppid()는 현재 실행 중인 프로세스의 부모 프로세스 ID를 반환해야 한다.
xv6는 이미 getpid() 시스템 콜을 구현하고 있으며, getppid()는 그 구조를 참고하여 구현 가능하다. (Tip)
1.1 구현 방향을 위한 탐색
sysproc.c 파일에서 sys_getpid의 구현을 확인할 수 있었다.
uint64
sys_getpid(void)
{
return myproc()->pid;
}여기서 myproc()의 정의를 찾았고, proc.c에 아래처럼 정의되어 있는 것을 확인하였다:
// Return the current struct proc *, or zero if none.
struct proc*
myproc(void)
{
push_off();
struct cpu *c = mycpu();
struct proc *p = c->proc;
pop_off();
return p;
}myproc() 함수는 현재 실행 중인 프로세스를 반환하며, 이 구조체 안에는 다양한 프로세스 관련 정보가 포함되어 있다.
myproc()의 return type은 proc 구조체의 포인터 형태인데, proc 구조체의 정의는 proc.h에 아래와 같이 정의되어 있었다.
// Per-process state
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID
// wait_lock must be held when using this:
struct proc *parent; // Parent process
// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
};여기서 proc 구조체가 자신의 부모 프로세스를 가리키는 포인터 parent를 가지고 있다는 점을 확인하였고, myproc()->parent->pid를 통해 부모 프로세스의 PID에 접근할 수 있다는 것을 알게 되었고, 이를 바탕으로 getppid()를 구현하기로 했다.
1.2 설계(Design) 요약
getppid()시스템 콜은myproc()을 통해 현재 프로세스를 얻고,- 그 안의
parent포인터를 따라가서pid값을 반환하면 된다. - 이 구현 방식은
getpid()와 매우 유사하며, 기존 코드를 그대로 참고하여 설계할 수 있다.
2 Implementation
위의 설계를 바탕으로 getppid() 시스템 콜을 xv6 커널에 새로 정의하고, 사용자 프로그램에서 호출할 수 있도록 전체 경로를 따라 구현하였다.
2.3부터 커밋 순서와 구현 순서가 일치하지 않는다. 이는 실제 개발 과정에서는 여러 에러를 접하고 해결하는 과정으로 진행했기 때문인데, 위키에서는 개발을 마치고 올바르다고 생각에 맞춰 순서를 작성하였다.
2.1 getppid() 함수 정의 및 선언
실제로 부모 프로세스의 PID를 반환할 함수 getppid()를 proc.c에 정의하였다.
int
getppid(void)
{
return myproc()->parent->pid;
}이후 defs.h에 선언을 추가하였다.
...
int getppid(void);2.2 시스템 콜 인터페이스 연결
유저 공간 상의 시스템 콜 요청을 처리하기 위해 커널 내부의 getppid()를 호출하는 sys_getppid() 함수를 sysproc.c에 추가하였다.
uint64
sys_getppid(void)
{
return getppid();
}이후 getppid() 시스템 콜에 고유 번호를 할당하였다. 이미 21번까지 정의되어 있어 22번을 할당하였다.
#define SYS_getppid 22이후 시스템 콜 고유 번호를 sys_getppid() 함수에 매핑되도록 시스템 콜 테이블에 등록하였다.
extern uint64 sys_getppid(void);
...
static uint64 (*syscalls[])(void) = {
...
[SYS_getppid] sys_getppid,
};
2.3 유저 인터페이스 등록
유저 공간 상에서 getppid() 함수를 시스템 콜로 호출할 수 있도록 stub 코드를 생성하는 스크립트를 추가하였다.
entry("getppid");또한 user.h에 함수 getppid() 함수 선언을 추가하였다.
int getppid(void);2.4 사용자 프로그램 작성 및 등록
시스템 콜을 실제로 사용하는 사용자 프로그램 ppid.c를 작성하였다.
#include "kernel/types.h"
#include "user/user.h"
int
main(void)
{
printf("My student ID is 2022123456\n");
printf("My pid is %d\n", getpid());
printf("My ppid is %d\n", getppid());
exit(0);
}최종적으로 ppid 프로그램을 Makefile의 UPROGS 항목에 추가하였다.
UPROGS=\
...
$U/_ppid\3 Results
3.1 컴파일 및 실행
아래와 같이 xv6를 클린 빌드한다.
make clean
make qemu3.2 사용자 프로그램 실행
ppid 명령어를 입력하면, user/ppid.c에서 작성한 내용이 실행된다:
$ ppid
My student ID is 2022123456
My pid is 3
My ppid is 2
3.3 동작 흐름
- 사용자 프로그램
ppid가 실행되면getpid()와getppid()시스템 콜을 호출한다. getppid()호출은usys.pl로 생성된 stub을 통해 커널로 시스템 콜을 트리거한다.- 시스템 콜 번호
SYS_getppid는syscall.c의 시스템 콜 테이블에서sys_getppid()로 매핑된다. sys_getppid()는proc.c에 정의된getppid()를 호출한다.- 최종적으로 현재 프로세스의
parent->pid값을 반환되어 출력된다.
4 Troubleshooting
4.1 getppid 함수가 선언되지 않음
user/ppid.c:9:29: error: implicit declaration of function 'getppid'; did you mean 'getpid'?유저 프로그램이 사용하는 함수는 user/user.h에 미리 선언되어 있어야 하는데, 이 부분이 누락되어 있어 추후에 추가하였다.
int getppid(void);4.2 사용자 프로그램이 xv6에서 인식되지 않음
$ ppid
command not found사용자 프로그램이 Makefile의 UPROGS 목록에 등록되지 않아서 루트 파일 시스템에 포함되지 않아 발생한 문제였다.
Makefile의 UPROGS 항목에 사용자 프로그램을 추가한다.
UPROGS=\
...
$U/_ppid\