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】再次测试

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】HTTPHTTPS

HTTP&HTTPS HTTP协议初识HTTP如何抓包Fiddler的使用抓包查看包的信息 报文格式请求报文响应报文报文对比 URLHTTP方法认识Header初识状态码 HTTPS协议为什么需要 HTTPS加密基础知识HTTPS的工作流程引入对称加密引入非对称加密引入证书HTTPS 的工作流程 浏览器从输入URL到展…

短视频剪辑从简单到复杂,这四款很OK!

作为一个刚刚踏入视频剪辑世界的新手&#xff0c;我最近可是忙得不亦乐乎。我尝试了四款流行的视频剪辑软件&#xff0c;今天&#xff0c;就让我来和大家分享一下我的使用感受&#xff0c;看看哪款软件更适合我们这些初学者。这里先说一句&#xff0c;选择视频剪辑软件就像挑衣…

opencv学习:calcHist 函数绘制图像直方图及代码实现

cv2.calcHist 函数是 OpenCV 库中用于计算图像直方图的函数。直方图是一种统计图像中像素值分布的工具&#xff0c;它可以提供图像的亮度、颜色等信息。这个函数可以用于灰度图像和彩色图像。 函数语法 hist cv2.calcHist(images, channels, mask, histSize, ranges, accumu…

解决 PyCharm 无法启动 Jupyter 服务器的问题:报错分析与解决方案

文章目录 报错背景报错详细信息解决方案pycharm 设置 报错背景 在使用 pycharm 付费版的过程中&#xff0c;发现一直无法启动 jupyter 服务器。 一直也不知道是为什么&#xff0c;直到在终端输入&#xff1a; jupyter notebook发现 jupyter 服务无法启动。 报错详细信息 下…

数据库系列之GaussDB数据库中逻辑对象关系简析

初次接触openGauss或GaussDB数据库的逻辑对象&#xff0c;被其中的表空间、数据库、schema和用户之间的关系&#xff0c;以及授权管理困惑住了&#xff0c;与熟悉的MySQL数据库的逻辑对象又有明显的不同。本文旨在简要梳理下GaussDB数据库逻辑对象之间的关系&#xff0c;以加深…

浅谈EXT2文件系统(1)

简介 EXT2&#xff08;Second Extended Filesystem&#xff09;文件系统是Linux操作系统的早期文件系统之一&#xff0c;它于 1993 年推出&#xff0c;是第一个旨在克服 Ext 文件系统限制的商业文件系统。EXT2 没有日志功能&#xff0c;EXT2 支持的单个文件大小为 2TB&#xf…

如何在Word中插入复选框

如何在Word中插入复选框&#xff1a;详细教程与技巧 在Word中插入复选框是一项非常实用的技巧&#xff0c;尤其是在制作问卷调查、待办事项清单、交互式表单或文档中需要用户进行选择时&#xff0c;复选框不仅能提高文档的功能性&#xff0c;还能显得更加专业。本文将详细讲解…

不小心把回收站清空了怎么恢复?别慌!四招找回

在日常使用电脑的过程中&#xff0c;我们可能会不小心清空回收站&#xff0c;从而丢失一些重要的文件。当遇到这种情况时&#xff0c;很多人可能会感到焦虑和无助。然而&#xff0c;幸运的是&#xff0c;有一些方法可以帮助我们尝试恢复这些被删除的文件。下面&#xff0c;我们…

大数据-133 - ClickHouse 基础概述 全面了解

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

关于【禁止new对象时在for循环内定义申明变量】

文章目录 简介代码分析反编译之后对比性能测试内存与垃圾回收情况JDK和常用框架怎么写总结依赖 简介 不知道是谁最先提出了一个不要将变量定义在循环内。 然后我们在代码扫描中有一项是&#xff1a;【禁止new对象时在for循环内定义申明变量】 我也好奇为什么不能&#xff1f…

如何利用MES系统进行产品质量全流程追溯

利用MES&#xff08;制造执行系统&#xff09;系统进行产品质量全流程追溯&#xff0c;是一个系统化和精细化的过程&#xff0c;主要涉及数据采集、信息整合、过程控制、查询分析以及持续优化等多个环节。以下是如何具体利用MES系统进行产品质量全流程追溯的步骤&#xff1a; 一…

centos(在线、离线)安装iptables

Iptables 是 Linux 操作系统中的一个用户空间工具&#xff0c;用来配置 Linux 内核中的 Netfilter 防火墙模块。它主要负责网络数据包的过滤、网络地址转换 (NAT) 以及配置防火墙规则。centos默认的防火墙管理工具是Firewalld&#xff0c;所以iptables需要下载安装。 目录 一…

嵌入式软件工程师:科技浪潮中的关键角色

嵌入式软件工程师&#xff1a;科技浪潮中的关键角色 一、嵌入式软件工程师的职业魅力 &#xff08;一&#xff09;市场需求旺盛 嵌入式软件工程师在当今科技领域中扮演着至关重要的角色。随着智能化时代的到来&#xff0c;嵌入式系统在各个行业的应用越来越广泛&#xff0c;市…

【贪心算法】贪心算法

贪心算法简介 1.什么是贪心算法2.贪心算法的特点3.学习贪心的方向 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.什么是贪心算法 与其说是…

[linux基础知识]教你使用vim和ctags阅读linux内核源码

1 安装ctags apt install ctags 2 内核源码目录下添加索引 使用下面命令&#xff0c;添加索引成功后&#xff0c;内核目录下会生成tags 索引文件。 ctags -R 3 vim使用索引阅读源码 跳转到函数变量定义与返回 #跳到函数或者变量定义 Ctrl] #返回 Ctrlo 光标移动到需要…

vue + Element UI table动态合并单元格

一、功能需求 1、根据名称相同的合并工作阶段和主要任务合并这两列&#xff0c;但主要任务内容一样&#xff0c;但要考虑主要任务一样&#xff0c;但工作阶段不一样的情况。&#xff08;枞向合并&#xff09; 2、落实情况里的定量内容和定性内容值一样则合并。&#xff08;横向…

设置使用阿里云服务器DNS

由于云服务器是从腾讯云迁移到阿里云&#xff0c;然后使用ssl验证时一直无法使用dns验证&#xff0c;也无法创建三级域名&#xff0c;原来需要把阿里云服务器改成阿里云的dns使用 如果使用其他服务器DNS会下面会显示当前DNS服务器&#xff0c;

Linux:git

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《Linux&#xff1a;git》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞&#xff01;&#xff01;&…

解决Docker镜像不可下载

使用国内可信的镜像中心 可信国内镜像网址&#xff1a;https://hub.atomgit.com/ 点击镜像仓库 搜索想要的镜像 按如图所示&#xff0c;即可查看对应的版本 点击复制&#xff0c;即可下载使用 缺点&#xff1a; 可用的镜像相比于docker官方量少 并且&#xff0c;获取的镜像名字…

【Java】方法2_Java的参数传递机制

文章目录 前言一、Java的参数传递机制都是值传递 1.基本类型的参数传递2.引用类型的参数传递总结 前言 学习Java的参数传递机制&#xff0c;基本类型的参数传递&#xff0c;引用类型的参数传递。 一、Java的参数传递机制都是值传递 值传递&#xff1a;指传输实参给方法的形参…