[Raspberry Pi] System Call을 통해 라즈베리파이에의 커널 프로그래밍을 실습해보자
in Devlog on Embedded System
System Call
시스템 콜은 응용프로그램에서 운영체제에게 어떠한 기능을 수행해 달라고 하는 하나의 수단
운영 체제의 커널이 제공하는 서비스에 대해, 응용 프로그램의 요청에 따라 커널에 접근 하기 위한 인터페이스
System Call의 기능
사용자 모드에 있는 응용 프로그램이 커널의 기능을 사용할 수 있도록 한다.
시스템 호출을 하면 사용자 모드에서 커널 모드로 바뀐다,
커널에서 시스템 호출을 처리하면 커널 모드에서 사용자 모드로 돌아가 작업을 계속한다.
Kernel Cross Compile
아래 내용에서는 라즈베리파이 3와 Ubuntu 16.04환경에서 크로스컴파일을 통해 라즈베리파이3에서의 시스템 콜을 다룹니다. 다만 커널 프로그래밍 시 다음을 주의해야 할 필요가 있습니다.
Kernel은 하드웨어 접근에 대해 어떠한 제한도 없기 때문에 커널에서의 오류는 시스템 에 치명적인 결과를 발생시킴 (Kernel panic)
함수 호출 등의 작업시 모든 에러 코드를 검사하고 처리해야 함
커널이 사용하는 stack의 크기는 제한되어 있고, 인터럽트 핸들러도 동일한 스택을 사용 하므로 큰 배열을 사용하거나, recursion이 많이 일어나지 않도록 주의
또한 실수 연산이나 MMX 연산을 사용할 수 없습니다.
System Call에 처리 함수를 등록하고 System Call 처리 함수를 구현해보겠습니다. 그리고 Kernel을 재 컴파일 한 후 라즈베리파이 system에 적용시켜봅시다!
먼저 커널 소스를 다운로드하고 커널 컴파일 환경을 하기위해 환경을 구축합시다.
# Download kernel source
ubuntu@ubuntu:~ $ mkdir working; cd working
ubuntu@ubuntu:~/working $ git clone -b rpi-4.4.y --depth=1 https://github.com/raspberrypi/linux
ubuntu@ubuntu:~/working $ cd linux
# Kernel compile configuration
ubuntu@ubuntu:~/working/linux $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
bcm2709_defconfig
# Kernel compile
ubuntu@ubuntu:~/working/linux $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage –j4
# Module compile
ubuntu@ubuntu:~/working/linux $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules \
–j4
# DTB(Device Tree Blob) compile
ubuntu@ubuntu:~/working/linux $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs –j4
# modules_install
ubuntu@ubuntu:~/working/linux $ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
INSTALL_MOD_PATH=../modules modules_install
ubuntu@ubuntu:~/working/linux $ cd ../modules
ubuntu@ubuntu:~/working/modules $ sudo rm lib/modules/4.4.50-v7+/build
ubuntu@ubuntu:~/working/modules $ sudo rm lib/modules/4.4.50-v7+/source
이후 라즈베리 파이 SD카드를 마운트 해봅시다. 마운트하는 자세한 내용은 전 포스트인 크로스 컴파일 구축에 포스팅되어 있습니다.
# Mount SD card
ubuntu@ubuntu:~/working/modules $ sudo mkdir /mnt/raspi
ubuntu@ubuntu:~/working/modules $ sudo mkdir /mnt/fs
ubuntu@ubuntu:~/working/modules $ sudo mount /dev/sdb1 /mnt/raspi
ubuntu@ubuntu:~/working/modules $ sudo mount /dev/sdb2 /mnt/fs
install한 라즈베리파이 linux kernel을 SD카드에 copy합니다.
# Copy kernel image(zImage)
ubuntu@ubuntu:~/working/modules $ cd ../linux
ubuntu@ubuntu:~/working/linux $ sudo cp arch/arm/boot/zImage /mnt/raspi/kernel7.img
# Copy modules
ubuntu@ubuntu:~/working/modules $ cd ../modules
ubuntu@ubuntu:~/working/modules $ sudo cp -r lib/modules/4.4.50-v7+/ /mnt/fs/lib/modules/
# Copy device tree blob
ubuntu@ubuntu:~/working/linux $ sudo cp arch/arm/boot/dts/*.dtb /mnt/raspi/
# Copy device tree blob overlays
ubuntu@ubuntu:~/working/linux $ sudo cp arch/arm/boot/dts/overlays/*.dtb* /mnt/raspi/overlays/
ubuntu@ubuntu:~/working/linux $ sudo cp arch/arm/boot/dts/overlays/README /mnt/raspi/overlays/
이후 SD카드를 언마운트 시키고 커널 이미지 외의 모듈 디렉토리 등 모든 정보들을 전송 시킵니다.
ssh 프로토콜을 이용하여 통신할 것이기 때문에 전송시킬 때 라즈베리파이의 ip주소를 입력해야 합니다. [192.168.0.26] 이렇게 말이죠
# Transmit kernel image
ubuntu@ubuntu:~/working/modules $ cd ../linux
ubuntu@ubuntu:~/working/linux $ scp arch/arm/boot/zImage pi@[RPi ip address]:/home/pi
# Transmit module directory. (kernel version 4.4.50-v7+)
ubuntu@ubuntu:~/working/linux $ cd ../modules
ubuntu@ubuntu:~/working/modules $ scp -r lib/modules/4.4.50-v7+/ pi@[Rpi ip address]:/home/pi
# Transmit device tree blob
ubuntu@ubuntu:~/working/modules $ cd ../linux
ubuntu@ubuntu:~/working/modules $ scp arch/arm/boot/dts/*.dtb pi@[RPi ip address]:/home/pi
# Transmit dtb overlays
ubuntu@ubuntu:~/working/linux $ scp arch/arm/boot/dts/overlays/*.dtb* pi@[RPi ip address]:/home/pi
ubuntu@ubuntu:~/working/linux $ scp arch/arm/boot/dts/overlays/README pi@[RPi ip address]:/home/pi
갈 길이 멉니다. 라즈베리파이에 sd카드를 연결한 후 카피한 커널 이미지와 모듈 디렉토리를 라즈베리파이의 부트 시스템 안으로 옮겨줍시다!
이후 라즈베리파이를 재부팅 하면 copy한 커널이미지를 로드시킬 것 입니다.
# Copy kernel image & module directory
pi@raspberry:~ $ sudo mv zImage /boot/kernel7.img
pi@raspberry:~ $ sudo mv 4.4.50-v7+/ /lib/modules/
# Copy device tree blob
pi@raspberry:~ $ sudo mv *.dtb /boot
# Copy dtb overlays
pi@raspberry:~ $ sudo mv *.dtb* /boot/overlays/
pi@raspberry:~ $ sudo mv README /boot/overlays/
# Reboot
pi@raspberry:~ $ sudo reboot
# Check kernel version
pi@raspberry:~ $ uname –r
커널 업데이트가 4.4.50-v7+로 업데이트 되었다면 여기 까지는 성공입니다..!!
이제 본격적으로 시스템 콜을 추가하여 실습해 봅시다.
unistd.h에 있는 시스템콜 호출 함수 목록에 NR_mysyscall 이라는 시스템 콜 함수를 추가시켜 보겠습니다.
먼저 시스템 콜 넘버를 등록합니다.
# Add System call number
ubuntu@ubuntu:~/working/linux $ vi arch/arm/include/uapi/asm/unistd.h
다음으로 해당 번호를 불러올 시스템을 설정합니다.
ubuntu@ubuntu:~/working/linux $ vi arch/arm/kernel/calls.S
이렇게 말이죠!
후…
그리고나서 시스템콜에서 불러올 함수를 syscall.h 파일 안에 선언해 줍니다.
# Register System call function
ubuntu@ubuntu:~/working/linux $ vi include/linux/syscalls.h
이후 호출될 시스템 콜에서 수행할 함수 내용을 적어봅시다. 저는 간단하게 parameter값을 연산하는 내용을 적어보았습니다.
printk는 커널안에서의 print함수라고 생각하시면 될 것 같습니다.
# System call code
ubuntu@ubuntu:~/working/linux $ vi kernel/mysyscall.c
이후 컴파일을 진행 할 Makefile코드를 작성해 봅시다.
# Makefile code
ubuntu@ubuntu:~/working/linux $ vi kernel/Makefile
이후 크로스 컴파일 합니다.
# Kernel compile
ubuntu@ubuntu:~/working/linux $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage –j4
커널쪽에서의 코딩은 완료가 된 것 같습니다.
이제 시스템 콜을 불러올 어플리케이션에서의 함수를 따로 작성 해 주어야겠죠?
시스템콜 넘버를 불러올 헤더파일을 추가 시키고 진행합니다.
# System call library
ubuntu@ubuntu:~/working/linux $ mkdir ../syscall; cd ../syscall
ubuntu@ubuntu:~/working/syscall $ vi newsyscall.c
어플리케이션 단에서의 프로그램을 작성합시다.
# System call application
ubuntu@ubuntu:~/working/syscall $ vi syscall_app.c
어플리케이션 단에서의 Makefile을 작성합니다.
# Makefile
ubuntu@ubuntu:~/working/syscall $ vi Makefile
이제 실행파일을 만들고 해당 파일을 라즈베리파이 SD카드로 옮깁시다!
다시 SD카드를 삽입시키고 마운트 시켜주세요.
점점 끝이 보이기 시작합니다 ㅎㅎ
# Make system call library & system call application
ubuntu@ubuntu:~/working/syscall $ make
# Check SD card
ubuntu@ubuntu:~/working/syscall $ cd ../linux
ubuntu@ubuntu:~/working/linux $ lsblk
# Mount SD card
ubuntu@ubuntu:~/working/linux $ sudo mount /dev/sdb1 /mnt/raspi
ubuntu@ubuntu:~/working/linux $ sudo mount /dev/sdb2 /mnt/fs
아까 했던 짓을 한번더 반복합니다.
커널 이미지, 실행파일 등등을 카피하고 Transmit시켜 줍시다
# Copy kernel image(zImage)
ubuntu@ubuntu:~/working/linux $ sudo cp arch/arm/boot/zImage /mnt/raspi/kernel7.img
# Copy syscall_app
ubuntu@ubuntu:~/working/linux $ cd ../syscall
ubuntu@ubuntu:~/working/syscall $ cp syscall_app /mnt/fs/home/pi
# Unmount SD card
ubuntu@ubuntu:~/working/syscall $ sudo umount /mnt/raspi
ubuntu@ubuntu:~/working/syscall $ sudo umount /mnt/fs
# Transmit kernel image(zImage)
ubuntu@ubuntu:~/working/syscall $ cd ../linux
ubuntu@ubuntu:~/working/linux $ scp arch/arm/boot/zImage pi@[Rpi ip address]:/home/pi
# Transmit syscall_app
ubuntu@ubuntu:~/working/linux $ cd ../syscall
ubuntu@ubuntu:~/working/syscall $ scp syscall_app pi@[Rpi ip address]:/home/pi
라즈베리파이를 켜고 커널 이미지를 reload 합니다.
# Change kernel image
pi@raspberry:~ $ sudo mv zImage /boot/kernel7.img
pi@raspberry:~ $ sudo reboot
실행파일을 실행시키면….
두근두근…
Success!
성공적으로 시스템콜을 이용한 크로스 컴파일을 완료 하였습니다.