前言
本文基于python3.5+, 树莓派型号为3b+
1. 关于树莓派的实时性
2. 几种思路与实现
2.1 提高优先级
提高优先级可以通过wiringpi
库提供的piHiPri(pri)
方法实现
其中,pri为int类型,取值范围为[0,99], 根据python中的实际测试,0为默认值,1优先级最高,99最小(此处貌似和资料不一致,但是实际测试是这样),使用方法为在执行前调用piHiPri()
方法,具体代码如下:
import wiringpi
wiringpi.piHiPri(pri)
2.2 绑定CPU
绑定cpu需要两个操作:
- 在系统中屏蔽掉一个或多个cpu
- 代码中绑定cpu
屏蔽cpu的实现方法:
/boot/cmdline.txt
中末尾添加isolcpus=x
其中x为屏蔽的核心 X=3时为树莓派中的最后一个cpu(树莓派3b为例,cpu编号为0-3)
此处注意,添加的isolcpus=x
不能起新行,需要写在原行的末尾,如:
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=3a7978f6-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait isolcpus=3
添加后重启系统,使用htop命令查看cpu占用(没有可以用apt-get install htop
安装),如下图:
此时系统自身调度不会再占用最后一个cpu(此处显示序号为4,因为是从1开始的,在系统中实际编号为3)
绑定cpu的方式:
python中,可以使用affinity
库来绑定制定cpu(没有可以用pip install affinity
安装),affinity
库一共提供了两个方法:
affinity.get_process_affinity_mask(pid)
affinity.set_process_affinity_mask(dwMask)
第一个方法传入pid,返回dwMask值,dwMask为CPU序号掩码,1(0001)代表只运行在CPU1,2(0010)代表只运行在CPU2,3(0011)代表可以运行在CPU1和CPU2,以此类推。
第二个方法传入dwMask, 返回0为成功,-1为失败.该方法需要root用户权限.
参考代码如下:
def run():
while True:
pass
p = multiprocessing.Process(target=run)
p.start()
pid = p.pid
print(affinity.get_process_affinity_mask(pid))
affinity.set_process_affinity_mask(pid, 15L)
print(affinity.get_process_affinity_mask(pid))
2.3 实时linux系统移植
出于通用性的考虑,本文只讨论linux系统的patch
2.3.1 RT-linux
Linux是典型的分时应用系统,对于实时性要求很高的应用,必须对内核本身动手术。而RTLinux则采取了一种比较聪明也比较折中的办法:他们实现一个最底层的精简的调度器,用于调度实时线程,原来的内核本身则成为实时调度器的一个优先级最低的任务。这样,当有实时任务时,普通内核已经建立于其上的普通进程被强制中断,实时线程被强制执行;只有当若有实时线程都让出cpu之后,普通内核才被运行,再由普通内核去调度执行普通的应用程序
移植方法:
(1)首先下载树莓派linux源代码:
git clone https://github.com/raspberrypi/linux.git
注意,由于该repo比较大,而git其实不支持断点续传(git init + git fetch + git checkout的方式实测并不能续传,每次断链再次fetch都是从头开始),所以建议只clone一个分支,且令depth为1,下载成功后再下载历史记录(如果需要),则命令改为:
git clone --depth=1 --single-branch --branch rpi-4.14.y git https://github.com/raspberrypi/linux.git
本文选用4.14,目前最新的树莓派内核版本为为4.14
(2)下载patch文件:
https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/4.14/
patch压缩包,在本地解压出.patch文件,并移动到clone好的linux/文件夹下即可
(3)给linux内核打patch
进入clone好的linux文件夹,执行命令
cat *.patch | patch -p1
(4)下载编译
git clone https://github.com/raspberrypi/tools ~/tools
(5)配置PATH变量,注意32bit和64bit系统存在差异(如果是其他版本的树莓派则不是arm-bcm2708,1代为):
32位系统:
echo PATH=\$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin >> ~/.bashrc
source ~/.bashrc
64位系统:
echo PATH=\$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin >> ~/.bashrc
source ~/.bashrc
sudo apt-get install libc6:i386
sudo apt-get install lib32stdc++6
sudo apt-get install lib32z1
如果文件夹或路径不同注意自己改名,zsh同理
另外一些文章提到了另外一些依赖,如果可能也尽量安装(并不知道有何种影响,一般的报错可以先行google查询):
sudo apt-get install bc
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install zlib1g:i386
sudo apt-get install libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5
(6)配置交叉编译设置
进入linux目录,修改Makefile文件
vim Makefile
找到如下语句
ARCH ?= arm
,修改为 :
CROSS_COMPILE ?= /home/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-
同样路径注意修改
(7)检查依赖
运行make menuconfig
查看是否有报错,根据报错信息安装依赖或自行google
(8)进行编译
KERNEL=kernel7
make bcm2709_defconfig
make zImage modules dtbs
如需多核加速编译可以加–j8
参数等
(9)部署到内存卡
插入sd卡和读卡器(格式化,分区等参考fdisk
等命令)
注意此sd卡已有rasbian系统
挂载:
mkdir mnt
mkdir mnt/fat32
mkdir mnt/ext4
sudo mount /dev/sdb1 mnt/fat32
sudo mount /dev/sdb2 mnt/ext4
安装:
sudo make INSTALL_MOD_PATH=mnt/ext4 modules_install
备份与拷贝内核:
sudo cp mnt/fat32/$KERNEL.img mnt/fat32/$KERNEL-backup.img
sudo cp arch/arm/boot/zImage mnt/fat32/$KERNEL.img
sudo cp arch/arm/boot/dts/*.dtb mnt/fat32/
sudo cp arch/arm/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
sudo cp arch/arm/boot/dts/overlays/README mnt/fat32/overlays/
sudo umount mnt/fat32
sudo umount mnt/ext4
(10)启动树莓派,检查是否安装成功
运行uname -a
查看是否有RT字段
2.3.2 PREEMPT_RT
2.4 利用中断
2.5 连接外设(实时钟发生器)
2.6 超频
3. 测试
影响延迟的变量包括:
- cpu是否满载(或其他cpu是否满载)
- 是否使用python(考虑到解释器的延迟)
3.1 提高优先级
3.2 绑定cpu
3.3 实时操作系统移植
3.4 利用中断
4. 总结
后记
参考
- 连接外设(MCU)
- c语言测试
- 交叉编译RT-linux
- linux绑定单核的处理
- RTLinux编程总结
- 树莓派内核交叉编译与升级
- PREEMPT_RT配置与内核编译
- Linux RT(1)-硬实时Linux(RT-Preempt Patch)在PC上的编译、使用和测试