운영체제 과제로 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을 남겨두면 정말 간단하게 되돌릴 수 있다.
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
이 명령어로 압축을 해제했더니 압축해제가 잘 안되서 오류가 나는거 같아 두번에 걸쳐 압축을 풀었다.
그리고
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 커널을 선택해서 부팅해주었다.(이 부분도 이후 이어질 시스템 콜 추가 과정에서 오류가 발생하지 않으려면 중요한듯)
마지막으로
uname -a
cat /etc/os-release
결과를 얻을 수 있었다.
/usr/src/linux/linux-5.19.12
경로에서
mkdir helloworld
cd helloworld
gedit helloworld.c
으로 가서 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 어쩌고 오류를 방지할 수 있다.)
그리고
gedit Makefile
위 경로에 Makefile을 만들어서
obj-y := helloworld.o
으로 ‘object 파일을 만들어주세요’라고 넣었다.
그리고 커널에서 인식할 수 있도록
이 경로에서
gedit Makefile
에서 core-y로 찾아서
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ helloworld/
이 곳에 helloworld를 위와 같이 추가했다.
시스템 콜 테이블인 syscall_64.tbl에 추가하기 위해
gedit arch/x86/entry/syscalls/syscall_64.tbl
파일을 열고
위와 같이 추가해주었다.
syscall.h에도 추가하기 위해
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
를 통해
결과를 얻을 수 있었다.
3번 과제로 만들어야 하는 syscall의 조건은 아래와 같다.
위의 자료형에 따르면 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 ..
로
이동한 후
이 경로에서
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
먼저 top 명령어로 활발한 프로세스의 pid를 정하고나서
pcount
는 현재 실행 중인 프로세스가 얼마나 오랫동안 cpu를 사용하고 있는지 나타내는 값을 의미하므로 TIME
의 0:56을 통해 확인할 수 있었다.