본문 바로가기
RaspberryPi

[RaspberryPi Kernel] Ubuntu에서 Raspberry Pi Kernel system call 함수 구현

by TYB 2024. 2. 15.
반응형

시작 전에 배경지식부터 함양


시스템 콜 함수란 user mode process와 kernel간의 interface-> kernel의 자료구조 및 HW에 대한 접근 불가

user mode process가 kernel이 가지고 있는 시스템의 상태 정보를 열람하거나 HW에 접근하여 HW를 통제하기 위해서는 kernel과의 communication channel이 필요함.


*POSIX API (Application Programming Interface)

유닉스 운영체제에 기반을 두고 있는 일련의 표준 운영체제 인터페이스. application이 시스템에 각 서비스를 요청할 때에 어떠한 함수를 사용해야 하는지 지정한 것.

 

표준을 두어 각각 다른 시스템에 응용 프로그램을 porting하는 것이 용이하게 하기 위한 목적이고 저수준 프로그래밍을 할 필요가 없도록 library화 해줬기 때문에 프로그래밍이 훨씬 쉬워짐.

open(), close(), read(), write() 등이 그 예시임.

 

*System Call

소프트웨어 인터럽트를 통해 커널에 서비스를 요청하는 것.

Linux에서는 POSIX API를 준수하는 library함수 안에서 system call 함수를 호출함으로써 system call을 사용한다.

 

시스템 콜 처리 절차

Libc표준 서브루틴 라이브러리에 들어있는게 open close read write 같은 함수임.


 

 

System Call 함수를 추가하는 목적은 기존 kernel에서 제공하지 않는 service를 user application에 제공하는 것임. 

비표준이기 때문에 현업에선 이렇게 짜서 올리면 안된다. 실습을 통해 Kernel영역의 system call이 어떻게 동작하는지 이해하는 것이 목적임.

표준 시스템콜을 쓰는게 아니라 나만의 시스템 콜함수를 만드는거임.

지금부터 System Call 함수를 추가해보겠다.

 

다른 곳에서 gpio를 사용하고 있다면 커널에서 gpio를 제어하는 코드를 넣어도 사용 중이라 참조가 안되겠죠?

 

고로 gpio를 menuconfig에서 해제시켜준다.

 

 

 

Device Driver 찾아서 엔터
GPIO Support 들어가주고
(sysfs interface) 체크해제
(Character device) 체크해제

 

device driver가 끝나면 다시 복원해놔야 아래와 같은 gpio 관련 라이브러리 사용이 가능함.

gpio 제어에 사용했던 shell 파일을 보면 /sys/class/gpio/exort를 불러와서 쓰는걸 볼 수 있음.

 

 

 


Flash Memory에 code를 write하는 과정을 fusing 이라고 함. 

Flash Memory는 erase를 할때  Memory에 1을 씀(퓨즈를 다 연결시키는 과정). 

SDMemory는 값이 막 바뀌지만 Flash Memory는 0으로 write된 값에 1을 못씀. 그래서 1로 erase 하는거임.

0x55를 메모리에 쓴다고 한다면 1부분은 유지 0을 써야될 부분의 1을 표현하는 퓨즈를 끊어버려서 0으로 만드는거임. 그래서 fusing이라고 하는거임.


 

1. System Call 번호 및 함수 등록

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ vi include/uapi/asm-generic/unistd.h

System call 함수에는 고유한 번호가 부여되어 있는데 나만의 함수를 만드려면 새로운 번호를 할당해 줘야겠죠?

열고 나서 /451을 쳐보면 표준 System call 함수의 마지막 번호가 나온다.

_NR 붙어있는건 prefix로 다 붙어있음.

open close read write도 있음.

 

 

452번을 쓰면 되겠쥬?

886 #define __NR_set_mempolicy_home_node 450
887 __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node)
888
889 #define __NR_mysyscall 451
890 __SYSCALL(__NR_mysyscall,sys_mysyscall)
891
892 #undef __NR_syscalls
893 #define __NR_syscalls 452

이걸 추가해주는거고
시스템 콜 함수의 갯수를 나타내는 친군데 1개 늘어났으니까 1 늘려주고

 

 

 

 

2. System Call 테이블에 System Call 처리 함수 등록

 

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ vi arch/arm/tools/syscall.tbl

 

 

자주 보이는 함수들이 보이죠?

G로 제일 아래로 내려가면 450번이 마지막 system call 함수 번혼데 우리꺼도 추가해줍시다.

467 451 common  mysyscall                   sys_mysyscall

 


system call 함수 원형을 header 파일에 넣어주고

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ vi include/linux/syscalls.h

G로 바닥으로 내려가주고

1389 asmlinkage long sys_mysyscall(long val);

 

 


헤더에 넣은 함수 원형 system call 실제 함수 구현

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ vi kernel/test_mysyscall.c

열어서 아래 코드 작성해주고

#include <linux/kernel.h>

asmlinkage long sys_mysyscall(long val)
{
    printk(KERN_INFO "Welcome to WCM's Embedded System!! app value=%ld\n",val);
    return val*val;
}

kernel에서 화면에 출력하는 함수는 printk 함수임!


 

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ vi kernel/Makefile

13번 라인 뒤에 추가해주고

obj-y는 매크로 명을 쓴거임.&nbsp; menuconfig에서

obj-$ 뒤의 (CONFIG_USERMODE_DRIVER)가 menuconfig에서 체크(선택)이 되어 있다면 y로 바뀌고 선택이 안되어 있으면 저렇게 풀매크로명으로 뜨는거임. 

+ obj-y 뒤에 있는 파일명들을 보면 커널이 어떤 역할을 하는지 알 수 있음.

kernel의 경우 의존성이 굉장히 높은 파일(헤더의 깊이가 깊다는 뜻)이 많고 따라서, 컴파일하는데 시간이 매우 오래걸린다.


이제 커널, 시스템콜 함수를 빌드

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ ./build.sh

컴파일이 잘 됬다면 object 파일이 생성되있을거임.  컴파일 에러가 나면 object파일이 없어유~

윗단계에 빼먹은게 있는지 확인

오류가 한번 났다면 make할 때 -j4 옵션을 빼주는게 좋음. 코어 4개로 돌리면 오류 발생 지점 확인이 어려움.

코어 1개로만 돌려야 어디서 오류가 났는지 확인이 용이함.

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ ls kernel/test_mysyscall.*
kernel/test_mysyscall.c  kernel/test_mysyscall.o

파이로 넘겨야겠죠

 

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ cp arch/arm/boot/zImage /srv/nfs/

 

 


라즈베리파이에서 nfs공유폴더 들어가고 zImage를 kernel 위치에 넣어주고 sync한번 쳐주고 재부팅

pi@pi08:/mnt/ubuntu_nfs$ ls -l zImage
-rwxrwxr-x 1 pi pi 7605312 Feb 15 16:18 zImage

pi@pi08:/mnt/ubuntu_nfs$ sudo cp zImage /boot/firmware/kernel7l.img
pi@pi08:/mnt/ubuntu_nfs$ sync
pi@pi08:/mnt/ubuntu_nfs$ sudo reboot

 

 

uname -a 를 쳐서  커널 시간 확인해주고 16:11 좋아요~

 

pi@pi08:~$ arm-linux-gnueabihf-

hyperbolic link 되어 있음.

pi@pi08:~$ ls -l /usr/bin/gcc
lrwxrwxrwx 1 root root 6 Jun  2  2023 /usr/bin/gcc -> gcc-12

pi@pi08:~$ ls -l /usr/bin/gcc-12
lrwxrwxrwx 1 root root 26 Jan 20  2023 /usr/bin/gcc-12 -> arm-linux-gnueabihf-gcc-12

 

 


3. User App 작성하기 (syscall_app.c) - 라즈베리파이에서 작성/빌드/실행

 

pi@pi08:~$ mkdir systemcall_test ; cd systemcall_test
pi@pi08:~/systemcall_test$ vi syscall_app.c

코드 쳐주고

#include <stdio.h>
#include <unistd.h>
#include <asm-generic/unistd.h>
#pragma GCC diagnostic ignored "-Wunused-result"
int main()
{
        long i;
        printf("input value = ");
        scanf("%ld",&i);
        i = syscall(__NR_mysyscall,i);
        if(i<0)
        {
                perror("syscall");
                return 1;
        }
        printf("mysyscall return value = %ld\n",i);
        return 0;
}

unistd.h는 시스템콜 함수가 정의되어 있는 부분이고 asm-generic/unistd.h는 우리가 정의한 시스템 콜 함수도 추가되어 있는거라 둘다 include 해줌.

scanf는 return을 받게 되어 있는데 return을 안받아주면 경고가 뜨므로 #pragma GCC diagnostic ignored "-Wunused-result"로 경고 무시하게 만들어놓고

syscall(__NR_mysyscall,i)에서 호출하는 함수는 아래에 있는 이 함수임.

#include <linux/kernel.h>

asmlinkage long sys_mysyscall(long val)
{
    printk(KERN_INFO "Welcome to WCM's Embedded System!! app value=%ld\n",val);
    return val*val;
}

이 함수에서 값 받아서 제곱해주고 return

 


우분투에서 작성한 파일 복사 후

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ cp include/uapi/asm-generic/unistd.h /srv/nfs/

 

pi@pi08:~$ cd /usr/include/asm-generic/

pi@pi08:/usr/include/asm-generic$ sudo mv unistd.h unistd.h_org

pi@pi08:/usr/include/asm-generic$ sudo cp /mnt/ubuntu_nfs/unistd.h .

 

아까 커널 업데이트 해놨으니까 system call 함수는 등록되어 있는거고

 

pi@pi08:~/systemcall_test$ gcc syscall_app.c -o syscall_app

pi@pi08:~/systemcall_test$ ./syscall_app
input value = 5
mysyscall return value = 25

 

dmesg를 치면 printk 출력메시지가 나오는데

부팅부터 지금까지 출력된 메시지가 원형큐에 저장되어 있음.

왼쪽에 괄호 안에 있는 숫자는 시간임 부팅후 745초가 지난거임.

 


윗 방법은 타겟보드에서 컴파일 한건데 ubuntu에서 라즈베리파이의 코드로 크로스컴파일해주는걸 해보겠다.

 

 

ubuntu@ubuntu8:/usr/include$ cp -r systemcall_test/ ~/pi_bsp/kernel/

ubuntu@ubuntu8:~/pi_bsp/kernel$ cd systemcall_test/

ubuntu@ubuntu8:~/pi_bsp/kernel/systemcall_test$ ls
syscall_app  syscall_app.c

 

gcc로 컴파일하는 헤더파일에는 unist.h가 복사되어 있지 않기 때문에 커널에 있는 헤더파일을 복사하지 않으면 오류가 남.

ubuntu@ubuntu8:/usr/include$ which arm-linux-gnueabihf-gcc
/usr/bin/arm-linux-gnueabihf-gcc

 

 

 

find 는 하나씩 파일을 찾기 때문에 하드디스크 용량이 크면 시간이 오래걸림.

그래서 새벽에 서버 자원이 놀고 있을 때 locate 라는 명령어를 동작하면 hdd에 db화해서 남겨놓기 때문에 찾는 속도가 훨씬 빠름.

 

 

sudo apt install locate

    패키지 받아주고

 

sudo updatedb

 이걸 통해서 db화해주고

 

locate unistd.h

를 치면 쭉 나오는데 그 중에

/usr/arm-linux-gnueabihf/include/asm-generic/unistd.h 이거임

arm의 library 파일들은 이 위치에 있는거임.

 

오리지날 복사본으로 만들어놓고

ubuntu@ubuntu8:/usr/local$ sudo cp /usr/arm-linux-gnueabihf/include/asm-generic/unistd.h /usr/arm-linux-gnueabihf/include/asm-generic/unistd.h_org

 

 

 

arm 크로스 컴파일 할 때 참고하는 라이브러리 위치 찾았으니까 해당 라이브러리에 kernel에서 사용하고 있는 unistd.h를 넣어주고 

 

ubuntu@ubuntu8:~/pi_bsp/kernel/linux$ sudo cp include/uapi/asm-generic/unistd.h /usr/arm-linux-gnueabihf/include/asm-generic/unistd.h

 

해주고 

 

폴더 위치 바꾸고 다시 컴파일 해주면

ubuntu@ubuntu8:~/pi_bsp/kernel/systemcall_test$ arm-linux-gnueabihf-gcc syscall_app.c -o syscall_app

 

빌드된 실행파일을 nfs를 통해 라즈베리로 넘겨주고 

ubuntu@ubuntu8:~/pi_bsp/kernel/systemcall_test$ cp syscall_app /srv/nfs/

 

 

라즈베리에서 실행


다음꺼도 꼭 해주세영~

 

[Raspberry Pi Kernel] Kernel menuconfig로 GPIO enable하고 다시 LCD 켜기

[RaspberryPi Kernel] Ubuntu에서 Raspberry Pi Kernel system call 함수 구현 시작 전에 배경지식부터 함양 시스템 콜 함수란 user mode process와 kernel간의 interface-> kernel의 자료구조 및 HW에 대한 접근 불가 user mode pr

program-developers-story.tistory.com

 

반응형