Linux_kernel驱动开发11

news2024/11/10 6:54:32

一、改回nfs方式挂载根文件系统

        在产品将要上线之前,需要制作不同类型格式的根文件系统

        在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统

        优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命

        【1】重启上位机nfs服务

        sudo service nfs-kernel-server restart

        【2】关闭上位机防火墙

        sudo service ufw stop

        【3】修改下位机的环境变量

        setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/nfs_share/_install ip=192.168.1.6:192.168.1.8:192.168.1.1:255.255.255.0 init=/linuxrc console=ttySAC0,115200 maxcpus=1 lcd=wy070ml tp=gslx680-linux

        【4】保存环境变量

        saveenv

        【5】重启

        re

二、Linux内核驱动开发的基础知识

        1、裸板驱动和内核驱动的区别

                1)裸板驱动

                        主观性较强,相当于英语考试中的作文模块

while (1) {
    // 代码逻辑
    ...
}
                2)Linux下的驱动开发

                        客观性较强,相当于英语考试中的完形填空

                        需要的知识:

                        【1】硬件的知识

                                读懂硬件原理图、读懂读写时序图、各种接口协议

                        【2】读取cpu数据手册

                                各种寄存器如何配置、各种外设如何配置

                        【3】驱动的编码框架

                                字符设备驱动(按字节访问,顺序固定)

                                块设备驱动(按块访问,顺序不固定)

                                网络设备驱动(按字节访问,顺序固定)

                        【4】内核态的编程规则

                                用户态和内核态的数据交互

                                内核模块的编程框架

                                解决竞态和并发

                                。。。

        2、Linux内核代码的特点

                1)介绍

                Linux内核本质上就是一个巨大的裸板程序,所有的函数都是自身实现的

标准C库系统调用Linux Kernel说明
fopenopensys_open打开文件
fcloseclosesys_close关闭文件
freadreadsys_read读文件
fwritewritesys_write写文件

                学习Linux内核最好的老师就是内核源码,遇到不会用的函数,去找内核源码。

                2)推荐书籍

                内核:<Linux内核的设计与实现>

                驱动:<LDD3>、<Linux设备驱动第三版>

        3、Linux内核需要注意的地方

                1)Linux内核不允许做浮点运算

                2)Linux内核中使用的是GUN C,不是标准C(GNU C是标准C的扩展版)

                3)Linux内核中每个线程都有两个栈

                        【1】用户态的栈

                        【2】内核态的栈

                4)Linux内核使用的内存空间是3G - 4G

                5)Linux内核更加注重代码的执行效率和可移植性

        4、搭建Linu内核的开发环境

                1)安装交叉编译器

                2)获取一份x6818上使用的Linux内核源码

                3)编译Linux内核源码

                4)制作并移植根文件系统

        5、使用source insight建立一个内核源码的项目工程

                source insight是一个阅读项目工程源码非常好用的工具

                官网:Source Insight Programming Editor and Code Browser

                1)安装source insight

                        序列号位置:sourcesightSN.txt

                2)添加Linux Kernel源码

                        【1】在window下某盘符中创建一个目录

                        【2】将Linux内核源码拷贝并解压到该目录

                        【3】打开kernel文件夹,新建文件夹,用来存储项目工程文件

                3)在source insight中添加项目工程

        6、编写内核的模块文件(*.ko)

                1)上位机编写模块文件

                【1】在虚拟机创建新目录,存放工程目录

                mkdir drivers

                【2】进入drivers目录

                cd drivers/

                【3】创建第一个工程目录

                mkdir hello_pro

                【4】进入hello_pro目录

                cd hello_pro/

                【5】编写工程文件hello.c

                vim hello.c

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");    // 声明开源,
                          // 如果不添加该声明,内核会报受到污染,因为没有遵守开源规则
MODULE_AUTHOR("Zjd");     // 声明作者

int __init hello_init(void)
{
    printk("<0>" "Hello, my owner kernel!\n");

    return 0;
}

void __exit hello_exit(void)
{
    printk("<0>" "bye, my owner kernel!\n");

    return ;
}

module_init (hello_init);
module_exit (hello_exit);

                【6】编写Makefile

                vim Makefile

obj-m += hello.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel

all:
    make -C $(KERNEL_PATH) M=$(PWD) modules

clean:
    make -C $(KERNEL_PATH) M=$(PWD) clean

                【7】编译工程文件

                make

                【8】将工程文件拷贝到共享的根文件系统中

                cp hello.ko /nfs_share/_install/

                2)下位机验证模块文件

                【1】查看内核中的模块

                lsmod

                【2】安装模块

                insmod hello.ko

                【3】卸载模块

                rmmod hello

        提示缺少 /lib/modules/ 目录

                mkdir /lib/modules

        提示缺少 3.4.39-embTwoGroup 目录

                mkdir /lib/modules/3.4.39-embTwoGroup

        卸载成功

        7、导出符号

                在C语言中,使用extern关键字修饰的符号可以跨模块访问

         进行内核态开发时,需要额外添加一个宏去修饰

        EXPORT_SYMBOL:它所修饰的符号,在内核中,所有的模块都可以访问到

        EXPORT_SYMBOL_GPL:它所修饰的符号,在内核中,只有遵循GPL规则的模块才能访问到

        1)上位机编写试验文件

                【1】创建新的项目工程

                mkdir export

                【2】进入实验目录

                cd extern_pro

                【3】创建文件

                touch export.c import.c export.h

                【4】编写程序

                vim export.h

#ifndef __EXPORT_H
#define __EXPORT_H

extern int add(int a, int b); 

#endif

                vim export.c

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int add(int a, int b)
{
    return a + b;
}

EXPORT_SYMBOL(add);

                vim import.c

#include <linux/init.h>
#include <linux/module.h>
#include "export.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int __init import_init(void)
{
    int ret = add(20, 6); 

    printk("<0>" "ret = %d\n", ret);

    return 0;
}

void __exit import_exit(void)
{
    printk("<0>" "I'm going.\n");

    return ;
}

module_init(import_init);
module_exit(import_exit);

                【5】编写Makefile

                vim Makefile

obj-m += export.o import.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
    make -C $(KERNEL_PATH) M=$(PWD) modules
    cp *.ko $(ROOTFS_PATH)

clean:
    make -C $(KERNEL_PATH) M=$(PWD) clean
        2)下位机验证

                【1】挂载export.ko

                insmod export.ko

                【2】挂载import.ko

                insmod import.ko

                【3】卸载import

                rmmod import

                【4】挂载export

                rmmod export

                【5】注意事项

        1】安装和卸载模块的顺序是相反的

        2】安装时要填写 *.ko 文件,卸载时直接填写文件名 *

        8、printk

                1)简介

                printk是内核中的打印函数,它输出到内核自己维护的缓冲区。

        printk的使用方法:

        printk("<0/1/2/3.../7>" "info");

注释:

<0/1/2/3.../7>:代表该条消息的打印优先级,越小优先级越高

info:代表要打印的消息

它的用法与printf相同,只不过前面多了一个消息优先级的配置,且不需要用 ',' 分开

                2)特殊情况

        printk("ret = %d\n", ret);        // 使用的是默认优先级

Linux内核将打印优先级设定为8个等级(0~7),值越小,优先级越高

printk输出的信息先到内核维护的缓冲区

缓冲区的内容能不能输出到控制终端是有限制的

                3)限制

        在uboot中设置的bootargs环境变量中的loglevel代表Linux内核设置的优先级阈值

当我们设定printk的优先级大于loglevel所设置的优先级,则可以打到终端,相反则不可以打印到终端。

                4)验证

        【1】创建实验目录

        mkdir printk_pro

        【2】进入实验目录

        cd printk_pro/

        【3】编写程序

        vim myprintk.c

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int __init printk_init(void)
{
	printk("<0>" "Level 0!\n");
	printk("<1>" "Level 1!\n");
	printk("<2>" "Level 2!\n");
	printk("<3>" "Level 3!\n");
	printk("<4>" "Level 4!\n");
	printk("<5>" "Level 5!\n");
	printk("<6>" "Level 6!\n");
	printk("<7>" "Level 7!\n");

	return 0;
}

void __exit printk_exit(void)
{
	return ;
}

module_init(printk_init);
module_exit(printk_exit);

        【4】查看内核源码

        在Source Insight中查找printk

        【5】优先级说明

宏名宏值说明
KERN_EMERG"<0>"系统不可用
KERN_ALERT"<1>"立即操作
KERN_CRIT"<2>"临界条件
KERN_ERR"<3>"错误
KERN_WARNING"<4>"警告
KERN_NOTICE"<5>"正常但重要
KERN_INFO"<6>"消息
KERN_DEBUG"<7>"调试

        【6】更改程序内容

        vim myprintk.c

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int __init printk_init(void)
{
	printk(KERN_EMERG "Level 0!\n");
	printk(KERN_ALERT "Level 1!\n");
	printk(KERN_CRIT "Level 2!\n");
	printk(KERN_ERR "Level 3!\n");
	printk(KERN_WARNING "Level 4!\n");
	printk(KERN_NOTICE "Level 5!\n");
	printk(KERN_INFO "Level 6!\n");
	printk(KERN_DEBUG "Level 7!\n");

	return 0;
}

void __exit printk_exit(void)
{
	return ;
}

module_init(printk_init);
module_exit(printk_exit);

        【7】编写Makefile

        vim Makefile

obj-m += myprintk.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
    make -C $(KERNEL_PATH) M=$(PWD) modules
    cp *.ko $(ROOTFS_PATH)

clean:
    make -C $(KERNEL_PATH) M=$(PWD) clean

        【8】下位机验证

------------------------------------------------

查看内核优先级设置

cat /proc/sys/kernel/printk

第一个值:优先级阈值(这里是2)

第二个值:内核默认优先级(这里是4)

------------------------------------------------

        9、模块参数(三步法)

        在C语言中,我们使用argc、argv给程序传参

./a.out xxx yyy zzz

// int main(int argc, char **argv)

int main(int argc, char *argv[]){

        ...

};

                1)定义全局变量
int irq = 0;
char *str = "Hello World!";
int fish[10] = {0};
int num = 10;

int __init module_param_init(void)
{
    ...;
    return 0;
}
                2)通过特定的宏

module_param(name, type, perm)

name:要声明为模块参数的变量名

type:变量的类型

perm:权限

module_param_array(name, type, nump, perm)

name:要声明为模块参数的数组名

type:数组元素的类型

nump:数组元素个数指针

perm:权限

                3)使用模块参数

        insmod module_param.ko

        insmod module_param.ko irq=10 str="easthome" fish=1,2,3,4,5,6

                4)验证

        【1】创建工程目录

        mkdir module_param

        【2】进入工程目录

        cd module_param

        【3】编写程序

        vim module_param.c

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int irq = 0;
char *str = "hello world!";
int len = 10;
int arr[10] = {0};

module_param(irq, int, 0644);
module_param(str, charp, 0);
module_param_array(arr, int, &len, 0644);

int __init module_param_init(void)
{
	int i = 0;

	printk(KERN_EMERG "irq = %d\n", irq);
	printk(KERN_EMERG "str = %s\n", str);

	// Linux Kernel apply the define of ARRAY_SIZE 
	// to count the number of members
	// #define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0])
	for (i = 0; i < ARRAY_SIZE(arr); i++) {
		printk(KERN_EMERG "arr[%d] = %d\n", i, arr[i]);
	}

	return 0;
}

void __exit module_param_exit(void)
{
	printk(KERN_EMERG "bye ~\n");

	return ;
}

module_init(module_param_init);
module_exit(module_param_exit);

        【4】编写Makefile

        vim Makefile

obj-m += module_param.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

        【5】下位机验证

        模板参数在系统中有负责维护的节点

        /sys/module/module_param/parameters/

        【6】思考

        我们在驱动代码里面定义了3个全局变量,这里只出来两个,arr、irq

        因为我们将str全局变量的权限设定为0,所以没有显示

        我们给irq与arr全局变量设定的权限就是该节点文件的权限

        【7】意义

        我们可以在内核中验证寄存器的功能,将寄存器声明为模块参数,在安装模块的时候就可以使用模块参数(REG=VAL)

        a、系统调用

                系统调用是用户进入内核空间的一种方式(还可以通过中断进入内核空间)

        1)意义

        【1】用户空间到内核空间

                是用户空间调用内核空间函数的一种方式

        【2】安全

                系统调用保证了内核的安全,允许应用程序调用内核中的函数(以安全的方式)

        2)系统调用的实现

        1】应用程序首先使用适当的值,填充寄存器

        2】调用特殊的指令

        3】执行指令,跳转到某个位置

        4】在该位置,根据填充到寄存器的值,找到内核中对应的函数

        5】调用该函数

        6】函数执行完毕后,原路返回到用户空间

        3)验证

        【1】应用程序选取适当的值

        vim kernel/arch/arm/include/asm/unistd.h

        【2】填充寄存器

        vim kernel/arch/arm/kernel/entry-common.S

        寄存器:r7

        【3】调用特殊指令

        vim kernel/arch/arm/kernel/entry-common.S

检查系统调用号

去系统调用表中的宏进行匹配

输出调用号,跳转到asm_syscall()

        vim kernel/arch/arm/kernel/traps.c

        vim arch/arm/kernel/calls.S

        4)演示

        【1】新增一个系统调用号

        vim kernel/arch/arm/include/asm/unistd.h

        【2】新增一个内核中的API(接口)函数

        vim kernel/arch/arm/kernel/sys_arm.c

        【3】修改系统调用表

        vim kernel/arch/arm/kernel/call.S

        【4】重新编译内核

        make uImage

        【5】重新烧录内核

        cp arch/arm/boot/uImage /tftpboot

        tftp 48000000 uImage

        mmc write 48000000 2000 3000

        re

        【6】系统调用syscall

        【7】编写测试程序

        mkdir sys_call

        vim sys_add.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

#define SYS_ADD_NUM		378

int main(void)
{
	int ret = 0;

	ret = syscall(SYS_ADD_NUM, 20, 6);

	printf("ret = %d\n", ret);

	return 0;
}

        【8】编译sys_add.c文件

                arm-cortex_a9-linux-gnueabi-gcc sys_add.c -o sys_add

        【9】下位机测试

                cp sys_add /nfs_share/_install/

        【a】调试

                1】查找交叉编译链工具位置

        which arm-cortex_a9-linux-gnueabi-gcc

                2】进入gcc的库目录

        cd /opt/toolchains/arm-cortex_a9-eabi-4.7-eglibc-2.18/arm-cortex_a9-linux-gnueabi/lib

                3】拷贝需要的库文件根文件系统

        cp libgcc_s.so.1 /nfs_share/_install/lib/

        【b】再次测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2118850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

业务架构解构与实践

往期回顾 >> CIO要懂业务架构&#xff0c;再谈业务架构的定义与作用&#xff0c;附业务架构教程下载 为什么要坚持先业务后IT&#xff1f;附71页PPT:企业架构及典型设计 为什么说“业务架构师”是ITBP的最佳人选&#xff1f; 业务架构之建模方法 业务架构的伴侣&…

线程安全问题和锁

所属专栏&#xff1a;Java学习 1. 线程的状态 新建&#xff08;New&#xff09;状态&#xff1a;当一个线程对象被创建&#xff0c;但还未调用 start () 方法启动时&#xff0c;处于新建状态。此时线程仅仅是一个 Java 对象&#xff0c;系统尚未为其分配资源。 就绪&am…

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入&#xff08;Embedding&#xff09;方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节&#xff1a;嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以…

Model-based RL动态规划(基于价值、基于策略,泛化迭代)

白盒环境和黑盒环境 白盒环境&#xff1a;知道环境的状态转移函数P(s’|s)或P(s’|s,a)和奖励函数R(s)或R(s,a)&#xff1a;   白盒环境下的学习相当于直接给出了有监督学习的数据分布&#xff08;就是有了目标靶子&#xff09;&#xff0c;不需要采样了&#xff0c;直接最小…

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中&#xff0c;我们经常会遇到这样的情况&#xff1a;一不小心&#xff0c;桌面上的某个重要文件被删除了。这时&#xff0c;大多数人可能会感到惊慌失措&#xff0c;不知所措。 其实&#xff0c;不必过于担心&#xff0c;因为有很多方法可以帮助我们找回…

@开发者极客们,网易2024低代码大赛来啦

极客们&#xff0c;网易云信拍了拍你 9月6日起&#xff0c;2024网易低代码大赛正式开启啦&#xff01; 低代码大赛是由网易主办的权威赛事&#xff0c;鼓励开发者们用低代码开发的方式快速搭建应用&#xff0c;并最终以作品决出优胜。 从2022年11月起&#xff0c;网易低代码大赛…

构建数字产业生态链,共绘数字经济新蓝图

在当今数字化浪潮席卷全球的时代&#xff0c;构建数字产业生态链成为了推动经济发展的关键引擎。数字产业生态链如同一个强大的磁场&#xff0c;吸引着各类创新要素汇聚&#xff0c;共同描绘出数字经济的宏伟新蓝图。 数字产业生态链的核心在于融合与协同。它将软件开发、数据分…

【原创】java+springboot+mysql学生信息管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时&#xff0c;发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null&#xff0c;再判断Count。 看了一下Count的源码如下&#xff1a; 1 [__DynamicallyInvokable] 2 public int Count 3 { 4 [__…

做了十几个数字化项目后,我对数字化转型的思考

现在讲数字化转型多是自顶向下视角&#xff0c;但企业仅提战略、目标还不够&#xff0c;必须把任务分配到每个团队及个人每天的工作中&#xff0c;这些工作与员工以前的工作不同&#xff0c;意味着团队和员工也要转型。 企业数字化转型口号再响亮&#xff0c;若一线员工工作内…

如何使用gewechat开发微信机器人

简介&#xff1a;本文将指导你如何搭建一个微信机器人&#xff0c;通过接入gewe实现智能回复与聊天功能。我们将从基础设置开始&#xff0c;逐步讲解如何配置机器人&#xff0c;并通过实例展示其实际应用。 随着人工智能技术的不断发展&#xff0c;智能机器人已经成为我们日常…

传输层协议TCP

本篇详细的讲解了有关传输层协议中 TCP 的常见知识&#xff0c;其中主要包括&#xff1a;TCP 协议段格式、确认应答机制、超时重传机制、连接管理机制&#xff08;三次握手和四次挥手&#xff09;、TIME_WAIT 和 CLOSE_WAIT 状态、滑动窗口、流量控制、延迟应答、捎带应答、面向…

宠物空气净化器352和希喂哪个品牌比较好?352、希喂宠物空气净化器终极PK

家里猫咪掉毛太厉害了怎么办&#xff1f;我家长毛、短毛猫都有&#xff0c;短毛还是猫界蒲公英银渐层&#xff0c;不会再有比曾经的我更受掉毛困扰的铲屎官了&#xff01;我曾经为了减少我家猫咪掉毛&#xff0c;鱼油、维生素A、维生素B5、碘营养物质都在喂&#xff0c;没啥用&…

table表格左键双击,单元格可编辑效果

1) 效果&#xff0c;修改内容后数据同步修改: 2) 思路 1、el-table提供了左键双击事件。 2、左键双击后&#xff0c;该单元格更改为input框后&#xff0c;input框需要获取焦点。 3、输入内容后&#xff0c;(回车按钮或者点击其他位置input框失去焦点)&#xff0c;数据需要更…

Django学习实战篇三(适合略有基础的新手小白学习)(从0开发项目)

前言&#xff1a; 在上一章中&#xff0c;我们对Django的Model层有了比较全面的认识&#xff0c;本章就来配置Django自带的admin。这里需要认识到&#xff0c;Django的Model层是很重要的一环&#xff0c;无论是对于框架本身还是对于基于Django框架开发的大多数系统而言。因为一…

私域电商 IP 化发展的探索与优势

摘要&#xff1a;本文聚焦于私域电商与社交电商的区别&#xff0c;重点探讨私域电商的 IP 属性。深入分析其在获取流量、转化用户以及挖掘用户价值方面的独特优势。同时引入链动 2 1 模式、AI 智能名片、S2B2C 商城小程序源码等元素&#xff0c;详细阐述这些元素在私域电商 IP…

前端:JavaScript中的this

前端&#xff1a;JavaScript中的this 1. this的指向2. 指定this的值3. 手写call方法4. 手写apply方法5. 手写bind方法 1. this的指向 在非严格模式下&#xff0c;总是指向一个对象&#xff1b;在严格模式下可以是任意值。 开启严格模式&#xff0c;如果是为整个脚本开启&#…

mapActions辅助函数的使用

什么是mapActions? mapActions 是 Vuex 提供的一个辅助函数&#xff0c;它允许你将组件中的方法映射为 Vuex 中的 Actions&#xff0c;以便于你可以直接从组件内部调用这些 actions。通过使用 mapActions 你可以在组件中以函数的方式引用 Vuex 中的 Actions&#xff0c;从而避…

个人随想-一道简单的AI面试题

大模型的兴起&#xff0c;很多公司现在都开始进入AI开发的新篇章&#xff0c;那么或多或少​也需要招聘一些AI的开发人员。 其实很多公司需要的&#xff0c;说白了就是一个AI开发工程师或者架构师&#xff0c;但是在招聘过程中&#xff0c;或多或少对要求写的太过夸张&#xf…

​在乙游热潮中,Soul App创新社交玩法,寻找年轻人的精神共鸣

经过8月份的连续事件,社会公众对于乙女游戏(下文简称乙游)有了全新的认知。 起初,是女子举重奥运冠军罗诗芳在社交平台与热门乙游《恋与深空》的意外互动,引发了广泛关注。“奥运冠军也玩乙游”成为社交平台热搜话题,众多玩家讨论奥运冠军是否会与他们有共同的虚拟角色喜好。 …