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

相关文章

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Linux修改pip临时目录方法的详解

《Linux修改pip临时目录方法的详解》在Linux系统中,pip在安装Python包时会使用临时目录(TMPDIR),但默认的临时目录可能会受到存储空间不足或权限问题的影响,所以本文将详细介绍如何... 目录引言一、为什么要修改 pip 的临时目录?1. 解决存储空间不足的问题2. 解决权限问题3. 提

Linux中的进程间通信之匿名管道解读

《Linux中的进程间通信之匿名管道解读》:本文主要介绍Linux中的进程间通信之匿名管道解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基本概念二、管道1、温故知新2、实现方式3、匿名管道(一)管道中的四种情况(二)管道的特性总结一、基本概念我们知道多