一 回忆昨天的内容
移植Linux内核,阅读Linux内核源码
make V=1
head.S
linux内核的特点
linux内核的7大子系统(功能)
linux内核的配置
make x6818_defconfig
cp arch/arm/configs/x6818_defconfig .config
linux内核的编译
make menuconfig
make config ---> 不推荐
流程: 启动uboot --> 硬件的初始化 -->
执行bootcmd中的命令 --> 加载linux内核 --->
检查cpuid是否支持 ---> 创建页表 --> 开启mmu --->
创建内核线程 ---> 执行bootargs 中的命令 --->
执行1号进程 --> 执行后续的进程 ---> 启动shell --->
解析用户输入的命令
二 掌握大名鼎鼎的NFS网络服务
2.0 rootfs_ext4.img ---> emmc
明确:各种应用程序属于根文件系统的一部分
如果将来要更新一个应用程序,需要将它放在
rootfs_ext4.img中
开发效率低,关键emmc的寿命就变短了。
采用NFS网络服务, 就是在上位机上部署一个根文件系统
rootfs,将来各种应用程序放在上位机上进行交叉编译
让下位机的linux内核到上位机找根文件系统
一旦找到,下位机就可以访问上位机中的各种应用程序了,
开发效率就会大大提高
2.1 具体实施步骤:
2.1.1获取最干净的内核:
rm kernel -rf
tar xf kernel.tar.bz2
cd kernel
2.1.2 如果是新板子, 老板子直接忽略这一步
需要网卡打一个补丁
cp /mnt/hgfs/easthome_porting/yt8511_support.patch ./
patch -p1 < yt8511_support.patch
2.1.3 配置ip:
下位机:192.168.1.6
ifconfig eth0 192.168.1.6
上位机:192.168.1.8
拿网线连接开发板和PC机,
需要桥接到网线所连接的网卡上
2.1.3 编译内核源码
make distclean
make x6818_defconfig
make uImage
cp uImage /mnt/hgfs/easthome_porting/fastboot
通过OTG_USB fabootboot 烧录uImage到emmc中
cp arch/arm/boot/uImage /mnt/hgfs/easthome_porting/fastboot
烧录
在uboot的命令行执行:
fastboot
win:fastboot flash boot uImage
2.1.4 上位机中需要安装NFS的服务
注意:提供虚拟机中已经安装过
sudo apt install nfs-kernel-server
上位机需要指定NFS服务一个共享目录
sudo vi /etc/exports
/opt/rootfs *(rw,sync,no_root_squash)
2.1.5 上位机获取根文件系统
sudo cp /mnt/hgfs/easthome_porting/rootfs_qt.tar.bz2 /opt
tar xf rootfs_qt.tar.bz2
2.1.6 重启上位机的nfs服务
sudo service nfs-kernel-server restart
2.1.7 在上位机的rootfs中准备一个测试程序
vi helloworld.c
2.1.8 重启下位机,修改bootargs
setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs ip=192.168.1.6:
192.168.1.8:192.168.1.1:255.255.255.0
init=/linuxrc console=ttySAC0,115200
tp=gslx680-linux lcd=wy070ml maxcpus=1
2.1.9 重启下位机,静静等待下位机的linux系统启动
cd /home
./hello
注意:切记下位机的"/" 就是上位机的/opt/rootfs
三 Linux内核态编程
3.1 内核态编程的特点
1) 内核中所有的函数都是linux内核自己实现的
2) 内核源码使用的是GNU C
GNU C 是标准C的扩展版
3) 更多关注的是执行的效率 以及可移植性问题
3.2 内核态编程时,需要注意的问题
1) 内核编程时使用的虚地址是0~4G
2) 内核中不允许做浮点运算
3) 每个线程对应独立的栈空间
每一个线程都有两个栈,
一个是内核态的栈,一个用户态的栈
3.3 编写独立的ko文件
做内核态的开发相当于英文中完形填空
做裸板开发相当于英文中的作文
vi hello.c
vi Makefile
//注意:all: 后面加tab,千万不要加空格
make
生成hello.ko
cp hello.ko /opt/rootfs
下位机中执行:insmod hello.ko //安装模块
[root@easthome-ly:/]# insmod hello.ko
[ 338.369000] hello world!
rmmod hello //卸载模块
[root@easthome-ly:/]# rmmod hello
[ 423.355000] bye bye!
lsmod 查看系统中已安装的模块
insmod xxxx.ko 安装模块
rmmod xxxx 卸载模块
3.4 导出符号
它是解决模块与模块之间的函数调用的问题
应用编程:
a.c
int add (int x, int y)
{
return x+y;
}
a.h
extern int add (int x,int y);
b.c
#include "a.h"
ret = add (a,b);
内核编程:
a.c
int add (int x,int y)
{
return x+y;
}
EXPORT_SYMBOL (add);
a.h
extern int add (int x, int y);
b.c
#include "a.h"
res = add (a, b);
vi export.c import.c Makefile
注意安装模块的顺序:
insmod export.ko
insmod import.ko
卸载的顺序:
rmmod import.ko
rmmod export.ko
3.5 关于 printk
printk 的标准用法:
printk ("<0/1/2/3/4/5/6/7>" "res = %d\n", res);
特殊情况:
printk ("res = %d\n", res);
默认的打印优先级 是4
linux内核将打印优先级设置为8级
值越小打印优先级越高
printk 输出的信息先输出到内核维护的输出缓冲区
输出缓冲区中的内容能不能打印到屏幕上是有限制的
printk 调用时使用的优先级 > 优先级的阈值
能够输出到屏幕
实际项目中有debug 和 release
一般是使用宏来控制
proc,是基于内存的虚拟文件系统
板子上的proc和pc上的proc目录不会做同步
用于向用户空间输出内核空间执行的状态
cat /proc/进程id/maps
cat /proc/cpuinfo
cat /proc/meminfo
cat /proc/sys/kernel/printk
上位机:
cat /proc/sys/kernel/printk
4 4 1 7
下位机:
cat /proc/sys/kernel/printk
3 4 1 7
第一个值:优先级的阈值开关
优先级高于该值的printk的信息可以输出到终端
第二个值:系统默认的打印优先级
printk ("res = %d\n",res)
3.6 模块参数
用户态:
./a.out xxx yyy zzz
int main (int argc, char *argv[]);
内核态:
安装模块时给其传递参数
内核中模块参数的使用步骤:
1) 定义一个全局变量
2) 通过特定的宏将其声明为模块参数
module_param (name, type, perm)
name, 要声明为模块参数的变量
type, 变量的类型
int long short charp (char *)
perm, 权限
module_param_array (name, type, nump, perm)
nump, 数组元素个数指针
3) 使用模块参数:
insmod xxx.ko
ls /sys/module/moduleparam/parameters
//是个临时文件
发现其中没有str,因为str权限是0
神奇的是,我们在用户空间更改了内核空间的值!
echo 10 >/sys/module/moduleparam/parameters/irq
4) 模块参数的使用场景
驱动代码的调试
VAR声明为参数
REG=VAR
3.7 谈谈对系统调用的理解:
3.7.1 系统调用的意义:
是用户空间调用内核空间函数的一种方式
由用户态进入内核态的一种方式
注:由用户态进入内核态的另一种方式是中断
保证了操作系统的安全性
允许应用程序调用内核中的函数
以安全的方式调用
3.7.2 系统调用是如何实现的?
每一个系统调用都有一个系统调用号
open ---> 5
close ---> 6
应用程序首先使用适当的值去填充寄存器
然后调用特殊的指令
执行该指令 导致跳转到内核的某一个位置去执行
在该位置根据填充到寄存器中的值
来找到内核中对应的函数 并调用该函数
函数执行完之后 原路返回到用户空间
//实现用户空间和内核空间的交互
open ("a.txt", O_RDWR);
vi arch/arm/include/asm/unistd.h
arm 中,这个所填充的寄存器是 r7
arm 中,这个特殊的指令是 swi
x86 int
固定的位置指的是:
软中断异常 跳转到异常向量表中
在跳转异常处理函数
arch/arm/kernel/entry-common.S
addne scno, r7, #__NR_SYSCALL_BASE
ENTRY(vector_swi)
//Get the system call number. 获取系统调用号
adr tbl, sys_call_table
sys_call_table [r7]
sys_call_table 位于calls.S中
尝试实现一个自己的系统调用,
完成内核和用户空间的交互。
Linux内核(Day21)
最新推荐文章于 2024-06-09 18:09:24 发布