Linux(野火-鲁班猫-笔记)
本文章作为记录。
本文章适用于刚接触rk鲁班猫的小白,方便快速入门,部分更详细内容未体现,见谅
因图片上传失败,附 文末有baidu 网盘 pdf文档链接
Linux 一些常用命令
-rwxrwxrwx
“-” 最前面代表是文件 若为d 则为目录
后面三组rwx分别为拥有者,同组和其他人的可读可写可执行权限
cp 复制
cp 源路径文件 目标路径 //复制文件到指定路径
cp -r 源路径文件夹 目标路径 //用于复制文件夹到指定路径
mv 移动
mv 源路径文件/文件夹 目标路径文件/文件夹 //移动文件/文件夹 到目标路径 注:“.”为当前路径
mv 源路径文件/文件夹 同一路径文件/文件夹 //修改文件夹/文件名字
mkdir 文件夹的创建
mkdir /当前文件夹/创建的文件夹名字
寄存器操作
例题:
利用16位寄存器的位操作实现下列功能
1)将寄存器BSRR的第6位置1 BSRR |= (1<<6);
2)将寄存器BSRR第六位清零 BSRR &=~(1<<6);
- 将寄存器BSRR第3 、6 位置1 BSRR|= (1 << 3) | (1 << 6);
4 )将寄存器BSRR第3 、6位清零 BSRR&=~( (1<<3) | (1<<6) );
STM32单片机 MCU
STM32F系列单片机 GPIO有哪些模式?
浮空输入、上拉输入、下拉输入、模拟输入、开漏输出、推挽输出、复用开漏输出、复用推挽输出、
gcc 编译过程
gcc -E hello.c -o hello.i //预编译
gcc -S hello.i -o hello.s //编译
gcc -c hello.s -o hello.o //汇编
gcc hello.o -o hello.c //链接
预编译:
预编译过程主要处理了 “#”那些源代码中的以"#“开始的预编译指令。比如”#include",“#define"等。主要的处理规则如下:
(1)将所有的”#define"删除,并且展开所有的宏定义。
(2)处理所有条件预编译指令,比如"#if",“#ifdef”,“#elif”,“#else”,“endif”
(3)处理"#include"预编译指令,将被包含的文件插入到该预编译指令的位置。
(4)删除所有注释"//“和”/* */"
(5)添加行号和文件名标识。比如#2"hello.c"2。
(6)保留#pragma指令,因为编译器必须使用它们。
编译:
存储在静态存储区的数据有哪些?
可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
代码区:存放函数体的二进制代码
文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
函数指针指向Code区,是程序运行的指令代码,数据指针指向Data,Heap,Stack区,是程序依赖以运行的各种数据
在文件作用域声明inline函数默认为static存储类型,const常量默认为static存储,如果加上extern,则为外部存储类型。
会话 (session)
会话是指一个终端用户与交互系统进行通讯的过程。
守护进程 创建步骤
1:创建子进程,并退出父进程
2:创建新会话
3:修改工作目录 //为根目录
4:设置权限掩码
5:关闭其他文件描述符
例子:
#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
pid_t p=fork();
if(p<0)
{
perror("fork failed\n");
exit(-1);
}
else if(p==0) //子进程
{
setsid(); //为子进程创建新会话
umask(0); ///**注释1*:---------------------------**
for(int i=0;i<3;i++) //文件描述符是非负整数,一般当系统打开时会占用0,1,2这三个文件描述符,将其关闭即可
close(i);
int fd=open("data.txt",O_RDWR);
while(1)
{
sleep(2); //每隔两秒就像文件中写入
write(fd,"hello world\n",11);
}
}
else //父进程
{
exit(0);
}
return 0;
}
注释1:*
静态库与动态库
库是一种二进制文件 (且没有main函数的)
**静态库:**是在程序执行前(编译)就加入到目标程序中去了
优点:运行快;发布程序无需提供静态库,因为已经在app中,方便移植;
缺点:大
**动态库:**是在程序执行时动态(临时)由目标程序去调用
优点:小
缺点:运行慢
静态库使用:
静态库的创建:
需要:带有主角函数的.c文件以及需要的各种.c非main文件
例:
//start.c
#include <stdio.h>
#include "fun.c"
void main()
{
funprintf("hello world!");
}
//fun.c
#include<stdio.h>
void funprintf(char *str)
{
printf("%s",str);
}
1.给库生成 .o文件
得到:
2.生成xxx.a静态库文件:
得到:
3.编译即可
得到
注意
- l是定制要用的动态库,库名砍头去尾;
- L告诉gcc编译器从-L指定的路径去寻静态库。默认是从/usr/lib或者/usr/local/lib去
另外,fun才是静态库名字,这点与window不太一样。
关于正点原子Linux alpha 开发板烧录问题
Linux 野火-鲁班猫——————————————↓
接下来将系统学习野火鲁班猫
鲁班猫是野火推出的高性能的卡片电脑。
编译(备注操作)
#清除之前生成的所有文件和配置
make mrproper
# 加载lubancat2_defconfig配置文件,rk356x系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat2_defconfig
# 编译内核,指定平台,指定交叉编译工具,使用8线程进行编译,线程可根据电脑性能自行确定
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
对于硬件信息
1.高主频(4核心 A55
2.大内存(1/2/4/8GB LPDDR4内存
3.大硬盘(8/32/64/128GB
4.强网络(2.5G OR 千兆
5.支持多显示(HDMI OR MIPI
6.高算力(1tops
主芯片RK3568,本教程针对
关于鲁班猫2与其他板载的对比
环境搭建
烧录
烧录分为SD卡烧录和EMMC烧录方式,我们的鲁班猫2可以有内置的EMMC,可以进行sd卡烧录和emmc烧录。那么。
SD卡-烧录软件
SDDiskToool RK官方烧录工具
因为我们一 般使用Ubuntu镜像,所以一般都是用该工具。
另外,我们需要sd卡的格式化工具
一般的,我们使用的Ubuntu版本为20.04
然后的,烧录文件命名中存在lite 表示无桌面版,有些其他教程也叫server版
然后是xfce版,即安装了xfce桌面的版本;xfce-full版,即在xfce基础上,安装了一些教育软件和一些办公软件等。
详细流程:
EMMC-烧录
容量8G、32G、64G、128G可选
RKDevtool2.9适用于Ubuntu
详细流程:
另外,关于烧录模式loader 和 maskrom
LINUX 的交叉编译器搭建
我们都知道Ubuntu有一个专门用来安装软件的工具apt,我们可以用它来全自动安装arm-linux-gcc。
此方法安装的是最新版的,如果你要安装的是某一特定版本的不推荐此方法!还要注意这个需要使用国内的apt软件源服务器,如果此方法不行的话就用方法二:
首先Ctrl+Alt+T弹出终端,使用如下命令进行arm-linux-gcc的安装:
sudo apt-get install gcc-arm-linux-gnueabihf
使用如下命令进行arm-linux-g++的安装:
sudo apt-get install g+±arm-linux-gnueabihf
如果要卸载时使用如下命令进行移除,arm-linux-gcc的卸载:
sudo apt-get remove gcc-arm-linux-gnueabihf
arm-linux-g++的卸载:
sudo apt-get remove g+±arm-linux-gnueabihf
神秘代码
sudo apt-get install curl python2.7 python-pyelftools git ssh make gcc libssl-dev liblz4-tool expect g++ patchelf chrpath gawk texinfo chrpath diffstat binfmt-support qemu-user-static live-build bison flex fakeroot cmake gcc-multilib g++-multilib unzip device-tree-compiler python3-pip libncurses-dev python3-pyelftools vim mtd-utils
Linux 内核文件夹
先放图
arch文件夹 架构目录,内部有,摩托罗拉、arm、x86架构,通常根据所使用的版型确定使用架构,然后进行裁剪
block文件夹 块设备文件夹,驱动相关
crypto 文件夹 加密相关文件夹,如输入的密码存储加密等
Documentation 文件夹 帮助文件夹,可以查看如何使用内核文件夹等
drivers 文件夹 驱动文件夹,包括spi驱动、块设备驱动、iic驱动、网卡驱动等
firmware 文件夹 存放标准协议文件
fs 文件夹 文件系统,支持超多文件系统
include 文件夹 绝大部分头文件在此
init 文件夹 初始化相关,里面有个main.c,值得从此处看
ipc 文件夹 进程间通讯所需支持文件夹
kernel 文件夹 包括调度、电源管理、中断、定时器、软中断、看门狗等
lib 文件夹 库文件夹
mm 文件夹 内存管理文件夹,内存管理、内存申请、内存映射等
net 文件夹 网络协议相关,对应的驱动在上面的driver文件夹中
sample 文件夹 底层例程等
scripts 文件夹 该文件夹是帮助我们配置、裁剪整个linux所使用的,一些方法等
security 文件夹 安全相关文件夹,一般不动
sound 文件夹 声卡文件夹,包括声卡驱动等,把声卡驱动单独在此处,drivers中无声卡驱动文件
tools 文件夹 内核使用的工具文件夹
usr 文件夹 初始的usr文件夹
virt 文件夹 映射 地址映射、IO映射等
功能实现
1、基础功能
1.字符设备驱动
2.块设备驱动
3.网络设备驱动 无
2、框架功能
框架:host驱动和设备驱动
驱动表现就是 /dev/xxx文件 打开、关闭、读写
现在新的内核支持设备树 这个一个 .dts 文件 此文件 描述了板子(产品)的设备信息
注意:Linux操作系统内核和驱动程序运行在内核空间,应用程序在用户空间
那么,应用程序需要访问内核的驱动以达到实现功能的目的,必然需要放分内核空间,但是应用程序在用户空间,所以 可以 :系统调用、异常和陷入 访问内核,一般使用 系统调用 但是也不是直接调用系统调用,而是通过API函数进行调用系统调用。(如 POSIX、API、C库)
每个系统调用都有系统调用号,应用程序软中断调用时,也会指定系统调用号。
流程如下:例:点亮LED
首先是应用程序打开文件(/dev/led),写入1或0达到开关效果,然后关闭文件(close)。
如何和驱动交互:
所以我们驱动也需要对应的open函数、close和write函数
关于驱动分类
字符设备驱动(最多) gpio、iic、spi、usb。。。
块设备驱动 EMMC 块存储等
网络设备驱动
且一个设备不是说一定只属于某一类型。如 USB WIFI ,SDIO WIFI,属于网络驱动,存在usb和sdio,因此也属于字符设备驱动。
修改登陆后启动输出信息
在根文件系统中,找到/etc/update-motd.d文件夹,如果是其他sdk包,可能有不同(如:设置启动服务、以及相关文件夹(/etc、/var、/run文件夹等)),登录打印信息的方式右很多种。
设备树
设备树的作用就是描述一个硬件平台的硬件资源。这个“设备树”可以被bootloader(uboot)传递到内核, 内核可以从设备树中获取硬件信息。
单独的一个设备树文件是以.dtsi为后缀,而且,其他设备数也可以引用。
- DTS 是指.dts格式的文件,是一种ASII 文本格式的设备树描述,也是我们要编写的设备树源码,一般一个.dts文件对应一个硬件平台。在Linux源码的“arch/arm64/boot/dts/”目录下又根据芯片厂商进行分类。
- DTSI 是指由芯片厂商提供,是同一芯片平台“共用”的设备树文件。
- DTC 是指编译设备树源码的工具,一般情况下我们需要手动安装这个编译工具。
- DTB 是设备树源码编译生成的文件,类似于我们C语言中“.C”文件编译生成“.bin”文件。
- DTBO 是设备树叠加层编译生成的文件,可以对DTB进行叠加补充。
关于设备树的编译
一些基础的通用外设使用U-Boot的设备树来进行初始化,而不通用的部分通过在U-Boot阶段加载内核的设备树来初始化。 所以我们在修改设备树时,主要也是去修改内核的设备树。例如在LubanCat-RK系列的开发过程中, 就没有修改过U-Boot的设备树,直接使用了rk3568-evb板卡的设备树。
注意,设备树的编译需要SDK开发包
# 编译kerneldeb文件
./build.sh kerneldeb
# 编译extboot分区
./build.sh extboot
关于内存管理单元MMU
为了内存防止直接访问物理地址,所以引入MMU的概念,
MMU作用:
- 保护内存: MMU给一些指定的内存块设置了读、写以及可执行的权限,这些权限存储在页表当中,MMU会检查CPU当前所处的是特权模式还是用户模式,如果和操作系统所设置的权限匹配则可以访问,如果CPU要访问一段虚拟地址,则将虚拟地址转换成物理地址,否则将产生异常,防止内存被恶意地修改。
- 提供方便统一的内存空间抽象,实现虚拟地址到物理地址的转换: CPU可以运行在虚拟的内存当中,虚拟内存一般要比实际的物理内存大很多,使得CPU可以运行比较大的应用程序。
那么,访问MMU时会进行内存的访问,而且如果使用了两级的表,那么cpu需要访问3次内存,耗费cpu性能。
所以引入了TLB,TLB对已经访问过的地址进行存储,以便cpu下次访问时,更快回复,若填满,则使用round-robin算法找到一个条目并覆盖此条目。
地址转换函数
上面提到了物理地址到虚拟地址的转换函数。包括ioremap()地址映射和取消地址映射iounmap()函数。
烧录
首先确定文件夹
确定文件夹为sdk文件夹,通常该目录原芯片厂商会提供
一般的:
当然,基本的kernel文件夹 uboot文件夹是必须的
编译
1.u-boot编译
2.内核和设备树
3.recovery 编译
4.文件系统 注意 编译build root 即使用 ./build rootfs 命令
而最后的update.img为最后的需要烧录的文件镜像
此镜像为内核、设备树、文件系统的整合包
好,那么好接下来是 Ubuntu根文件定制
根文件定制
1.首先下载Ubuntu的文件夹 网站 https://cdimage.ubuntu.com/ubuntu-base/releases/20.04.1/release/ubuntu-base-20.04.1-base-amd64.tar.gz
这是20.04的,够用了
2.然后安装qemu 模拟器
apt-get install qemu-user-static
3.然后将刚安装的qemu这个东西 拷贝到刚刚下载的ubuntu文件夹(已解压(tar命令))中,
cp /usr/bin/qemu-aarch64-static system/usr/bin/
(注意,system文件夹中即为我们想要制定的根目录),大概这样:
4.然后准备网络,如下命令
cp -b /etc/resolv.conf system/etc/resolv.conf
//这些命令建议使用sudo
5.接下来我们将使用shell脚本,所以我们需要写一个mount.sh 命名不重要
代码如下
#!/bin/bash
mnt() {
echo "MOUNTING"
sudo mount -t proc /proc ${
2}proc
sudo mount -t sysfs /sys ${
2}sys
sudo mount -o bind /dev ${
2}dev
sudo mount -o bind /dev/pts ${
2}dev/pts
}
umnt() {
echo "UNMOUNTING"
sudo umount ${
2}proc
sudo umount ${
2}sys
sudo umount ${
2}dev/pts
sudo umount ${
2}dev
}
if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
umnt $1 $2
fi
6.给这个脚本权限 并执行该脚本 挂载
chmod +x mount.sh
./mount.sh -m system/
7.进入根文件系统进行操作:
sudo chroot system
8.更新 如果烧录进开发板中依然有网络设备树与驱动,且网络可连接,可以选择不更新
apt update
apt upgrade
9.至少需要安装一个 systemd,否则开机无法进入系统
apt install -y systemd
10.安装自己需要的功能 (可能很慢,不太建议)
apt install vim git ....(根据自己需求添加)
11.安装 xubuntu:
apt-get install xubuntu-desktop
//Q:若出现错误
Unable to locate package xxxx
A:可能是源的问题,替换好的源即可
安装包的源没有添加到 /etc/apt/source.list
中,导致无法识别安装包,可以自行添加源,也可以使用下面给出的 source.list
覆盖原来的 /etc/apt/source.list
文件:
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted
## Major bug fix updates produced after the final release of the
## distribution.
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted
## Uncomment the following two lines to add software from the 'universe'
## repository.
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team. Also, please note that software in universe WILL NOT receive any
## review or updates from the Ubuntu security team.
deb http://ports.ubuntu.com/ubuntu-ports/ bionic universe
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic universe
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates universe
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates universe
## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security universe
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security universe
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security multiverse
deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security multiverse
12.然后是 添加用户:
useradd -s '/bin/bash' -m -G adm,sudo zyl
//zyl 即为用户名
//给用户设置密码:
passwd zyl
13.设置root密码
//给root用户设置密码:
passwd root
//修改完自己的根文件系统就可以退出了。
exit
//一定要取消挂载:
./mount.sh -u system/
制作根文件
制作自己的根文件系统,大小依据自己的根文件系统而定,注意依据 temp
文件夹的大小来修改 count
值:
mkdir rootfs
dd if=/dev/zero of=rootfs.img bs=1M count=4000
mkfs.ext4 rootfs.img
sudo mount rootfs.img rootfs/
sudo cp -rfp system/* rootfs/
sudo umount rootfs/
e2fsck -p -f rootfs.img
resize2fs -M rootfs.img
rootfs.img为最终的根文件系统镜像文件
驱动正文
驱动结构
1.头文件
2.驱动加载函数
3.驱动卸载函数
4.许可证声明
5.模块参数
6.版本信息
以上即在XXX.c的c驱动文件中的
如图:
驱动操作
加载驱动
insmod xxx.ko
查看驱动
lsmod
卸载驱动
rmmod xxx.ko
加载驱动及其依赖
modprobe xxx.ko
驱动结构 (附加)——驱动传参
驱动支持传递参数类型:
基本类型:char bool int long short byte ushort uint
数组 array
字符串 string
如何传递?
驱动传递基本类型、传递数组、传递字符串,对应函数
module_param :传递基本类型参数
module_param_array:传递数组类型函数
module_param_string :传递字符串类型函数
以上三个传参函数在include/linux/moduleparam.h中有定义
一、传递基本类型参数函数
对于perm 参数的读写权限
读写权限在include/linux/stat.h和include/uapi/linux/stat.h
R:可读权限 W:可写权限 X:可执行权限 U(USR):指拥有者
G(GRP) :指同组用户 O(OTH):与文件拥有者不同组的
例子:S_IRWXU S_I不用看 RWX 指可读可写可执行 U指对拥有者操作
对于右侧的; S_IRWXUGO S_I不用看 RWX 指可读可写可执行 UGO 指对文件拥有者、同组者、以及与文件拥有者不同组的 即所有用户拥有可读可写可执行权限
常用为S_IRUGO 即所有用户拥有可读权限
二、传递数组类型函数
三、传递字符串类型参数给驱动
附 :描述模块参数函数
配置VSCODE环境
因为我们使用vscode编写,需要配置vscode环境,以便我们使用时有代码补全
之前如果出现vscode出现引入头文件出错的情况也可以根据此处进行相应配置
vscode 隐藏顶部标题栏
修改方法:
- 打开vscode的设置(File - Preferences - settings),搜索
window.titlebarstyle
, 修改为custom
,重启vscode即可生效:
一般需要引入的头文件位置在 sdk开发包/kernel内核包/include文件夹/linux文件夹
查看绝对路径
然后在需要的工程使用快捷键 CTRL+shift+P,选择C/C++ 编辑配置(json)
在includepath 下加一行即可路径即可,保存 (注意两个includepath 之间有逗号的)
例子
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
static int a=0;
static int array[5]={
0};
static int array_size;
static char str1[10]={
0};
module_param(a,int,S_IRUGO);
MODULE_PARM_DESC(a,"e.g a=1");
module_param_array(array,int,&array_size,S_IRUGO);
MODULE_PARM_DESC(array,"e.g array=1,2,3");
module_param_string(str,str1,sizeof(str1),S_IRUGO);//注意,此处str为输入的名字,如果使用驱动输入没有该字符串,则无效
MODULE_PARM_DESC(str,"e.g str=hello");
static int moduleparam_init(void)
{
int i=0;
printk("a id %d\n",a);
printk