System call 추가

boseung·2023년 5월 5일
0
post-thumbnail

운영체제 과제로 linux에 System call을 추가하는 과제를 받았는데, 컴파일 과정 중 오류 때문에 너무 긴 시간이 걸렸기 때문에 다른 사람들이 참고할 수 있도록 기록을 남기려고 한다.

과제는
1. Compiling Linux kerne
2. Adding my first system calls
3. Taking a glance at PCB via Syscalls
와 같았다.

2번 과제에서 특히 잔뜩 시간을 잡아먹었다. 거의 1주일

그래서 온갖 글들을 다 따라했었는데, 오류를 해결하는데 가장 큰 도움이 되었던 것은 이 유튜브 영상이었다.

커널 컴파일을 하면서 포맷을 끝없이 반복할텐데, 시간을 아끼기 위해서 VMware Work Station을 이용해서 초기 버전의 Snap Shot을 남겨두면 정말 간단하게 되돌릴 수 있다.

01. Compiling Linux kerne

VMware 가상환경을 이용해서 우분투 리눅스를 다운받았으며, 디스크용량은 100GB, CPU 코어는 8개, 메모리는 12GB로 정했다.(코어 4개도 너무 느려서..)

이후 https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.19.12.tar.xz 에서 커널 소스파일을 다운받고

sudo xz -d linux-5.19.12.tar.xz
sudo tar xvf linux-5.19.12.tar

명령어로 /usr/src/linux에 압축을 풀었다.(mkdir linux로 폴더를 만들었다.)
개인적으로 tar xvf linux-5.19.12.tar.xz 이 명령어로 압축을 해제했더니 압축해제가 잘 안되서 오류가 나는거 같아 두번에 걸쳐 압축을 풀었다.

그리고

경로1

cd linux-5.19.12
cp /boot/config-5.19.0-41-generic .config
make menuconfig

여기서 .config 파일을 load하고 save 후 나왔다.

그리고 오류방지를 위해서

sudo scripts/config --disable SYSTEM_TRUSTED_KEYS
sudo scripts/config --disable SYSTEM_REVOCATION_KEYS
sudo scripts/config --disable MODULE_SIG_ALL
sudo scripts/config --disable MODULE_SIG_KEY
sudo scripts/config --disable CONFIG_DEBUG_INFO_BTF

명령어들을 입력했다.

이제 여기서 VMware Workstation으로 snap 샷 찍어서 백업을 해두었다.(몇 번이나 포맷하고나서 알아낸 꿀팁…)

sudo make -j[core 수]

여기서 뭐라뭐라 뜰때마다 Enter 눌러서 컴파일 진행시켰다. (아마 위에서 disable한 설정과 관련된 거 같다.)

그리고 1시간 정도 지나고

sudo make modules_install -j[core 수]
sudo make install -j[core 수]
sudo update-grub

이후 재부팅 시 왼쪽 shfit키를 꾹 눌러서 안전모드로 들어가서 5.19.12 커널을 선택해서 부팅해주었다.(이 부분도 이후 이어질 시스템 콜 추가 과정에서 오류가 발생하지 않으려면 중요한듯)

커널 선택1
커널 선택2

마지막으로

uname -a
cat /etc/os-release

결과1

결과를 얻을 수 있었다.

02. Adding my first system calls

/usr/src/linux/linux-5.19.12 경로에서

mkdir helloworld
cd helloworld
gedit helloworld.c

helloworld경로

으로 가서 helloworld.c 파일을 만든 후

#include <linux/kernel.h>
#include <linux/syscalls.h>

SYSCALL_DEFINE0(helloworld) {
        printk("Hello, <Kang Bo Seung>!\n");
        printk("191639\n");
    
    return 0;
}

입력한 후 저장(여기서 SYSCALL_DEFINE0이 시스템 콜 매크로 함수를 이용하는 건데, not defined x86_sys 어쩌고 오류를 방지할 수 있다.)

그리고

경로3

gedit Makefile

위 경로에 Makefile을 만들어서

obj-y := helloworld.o

으로 ‘object 파일을 만들어주세요’라고 넣었다.

그리고 커널에서 인식할 수 있도록

경로55

이 경로에서

gedit Makefile

에서 core-y로 찾아서

core-y			+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ helloworld/

이 곳에 helloworld를 위와 같이 추가했다.

시스템 콜 테이블인 syscall_64.tbl에 추가하기 위해

경로5

gedit arch/x86/entry/syscalls/syscall_64.tbl

파일을 열고

추가1

위와 같이 추가해주었다.

syscall.h에도 추가하기 위해

경로6

gedit include/linux/syscalls.h

제일 마지막 줄에

asmlinkage long sys_helloworld(void);

을 추가해주었다.

이후 컴파일 해주기 위해

sudo make -j 8
sudo make modules_install
sudo make install
sudo update-grub

그리고 재부팅 한 후

위에서와 같이 검은 화면에서 왼쪽 shift를 눌러서 안전모드로 들어가서 컴파일한 커널로 변경해서 부팅했다.

이후

cd ~에서

mkdir hello
gedit hello.c

테스트 파일을 만들고

#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>

int main(){
	long int checkcall = syscall(451);
	printf("System call: sys_helloworld : returned %ld \n", checkcall);
	return 0;
}

저장한 후

gcc hello.c
./a.out
dmesg

를 통해

결과

결과를 얻을 수 있었다.

03. Taking a glance at PCB via Syscalls

3번 과제로 만들어야 하는 syscall의 조건은 아래와 같다.

  1. 특정 프로세스의 PID를 인자로 입력받고
  2. 해당 프로세스에 대한 PCB, 즉 리눅스의 경우 'task_struct' 구조체를 읽은 후
  3. 해당 구조체 내부에 있는 'sched_info'를 가져와
  4. 내부의 pcount 정보를 반환

자료구조

위의 자료형에 따르면 task_struct 구조체 안에 sched_info 구조체가 들어있는 구조이다.

그 후 sched_info안에 unsigned long 타입의 pcount를 가지고 있다. (여기서 pcount는 현재 실행 중인 프로세스가 얼마나 오랫동안 cpu를 사용하고 있는지 나타내는 값을 의미한다.)

그래서 과제의 힌트로 받은 find_task_by_vpid(pid)로 구조체를 받아왔다. 그 후 task포인터로 sched_info에 접근했다.

자세한 코드는 아래와 같다.

#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/sched/signal.h>
#include <linux/sched.h>

SYSCALL_DEFINE1(my_syscall, pid_t, pid)
{
    struct task_struct *task;
    struct sched_info schedinfo;

    task = find_task_by_vpid(pid);
    if (task == NULL) {
        return -ESRCH; 
    }

    schedinfo = task->sched_info;

    return schedinfo.pcount;
}

이후 과제 2번에서와 마찬가지로

mkdir my_syscall로 폴더를 만든 후

gedit my_syscall.c로 위의 코드를 넣은 후

해당 경로에 gedit Makefile파일을 만든후 아래의 내용을 추가했다.

obj-y := my_syscall.o

를 넣었다.

그리고 cd ..

이동한 후

경로8

이 경로에서

gedit Makefile

에서 core-y로 찾아서

core-y			+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ helloworld/ my_syscall/

이 곳에 my_syscall를 위와 같이 추가했다.

그리고 시스템 콜 테이블인 syscall_64.tbl에 추가하기 위해

gedit arch/x86/entry/syscalls/syscall_64.tbl

파일을 열고

추가했다.

syscall.h에도 추가하기 위해

gedit include/linux/syscalls.h

제일 마지막 줄에

asmlinkage unsigned long my_syscall(pid_t pid);

을 추가해주었다.

이후 컴파일 해주기 위해

sudo make -j 8
sudo make modules_install
sudo make install

그리고 재부팅 한 후

검은 화면에서 왼쪽 shift로 안전모드로 들어가서 컴파일한 커널로 변경해서 부팅했다.

이후

cd ~에서

mkdir syscall
gedit syscall.c

테스트 파일을 만들고

#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#define MY_SYS_PROCSCHED 452 
int main(int argc, char *argv[]) {
 unsigned long ret = syscall(MY_SYS_PROCSCHED, 1172);
 printf("pcound of 1172 = %lu \n", ret);
 return 0;
}

다음의 명령어로 테스트 했다.

gcc syscall.
./a.out
ps -f 1172

결과2

먼저 top 명령어로 활발한 프로세스의 pid를 정하고나서

pcount는 현재 실행 중인 프로세스가 얼마나 오랫동안 cpu를 사용하고 있는지 나타내는 값을 의미하므로 TIME의 0:56을 통해 확인할 수 있었다.

profile
Dev Log

0개의 댓글

관련 채용 정보