Linux_kernel驱动开发11

2024-09-09 16:12
文章标签 linux 开发 驱动 kernel

本文主要是介绍Linux_kernel驱动开发11,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、改回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/kernelall:make -C $(KERNEL_PATH) M=$(PWD) modulesclean: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_Hextern 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/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.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/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.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/_installall:make -C $(KERNEL_PATH) M=$(PWD) modulescp *.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		378int 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】再次测试

这篇关于Linux_kernel驱动开发11的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1151649

相关文章

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch