ARM——驱动——内核编译

news2024/12/25 8:55:14

一、内核的介绍

Linux内核是Linux操作系统的核心内容,它负责管理系统的硬件资源,并为上层的应用程序提供接口。(在上文都有所介绍)

功能:

  1. 进程管理:内核负责创建、调度、同步和终止进程。它还管理进程间的通信和数据传递,如管道、信号量和消息队列。
  2. 内存管理:内核负责分配和回收内存资源,确保每个进程都有足够的内存空间,同时防止一个进程访问另一个进程的内存空间。这包括内存分配、虚拟内存管理、内存保护等功能。
  3. 文件系统:Linux内核提供了多种文件系统的支持,如ext4、XFS、Btrfs、FAT32、NTFS等。它负责文件的创建、删除、读写、权限控制等操作。
  4. 设备驱动:内核包含了与硬件设备通信的驱动程序,这些驱动程序允许操作系统控制和管理各种硬件设备,如硬盘、显示器、键盘、鼠标等。
  5. 网络通信:内核实现了网络协议栈,支持TCP/IP和其他网络协议,使得Linux系统能够进行网络通信和数据传输。
  6. 安全性:Linux内核提供了多种安全机制,包括用户和组权限管理、访问控制列表(ACL)、安全模块(如SELinux)等,以保护系统安全。
  7. 虚拟化:内核支持虚拟化技术,如KVM(Kernel-based Virtual Machine),允许在单个物理机上运行多个虚拟机。
  8. 电源管理:内核负责管理电源使用,包括节能模式、休眠和唤醒等,以延长电池寿命和提高能效。
  9. 系统调用接口:内核提供了系统调用接口,允许用户空间的应用程序请求内核提供的服务,如打开文件、读取数据、执行系统命令等

二、Linux内核编译

1.1首先在官网中下载需要的源代库

The Linux Kernel Archives


 

本次使用的是2.6.32.2—mini2440

1.2解压下载好的文件(进入到文件所在的目录中进行解压)

 sudo tar -xvf   +文件名

1.3进入到文件目录

由于这里面的内容很多,文件也很多
所以需要使用makefile或者条件编译来编译
对于条件编译,通过#ifXX #endif来选择编译的对象
对于makefile

在.config文件中设置变量,控制makefile中的需要进行操作的文件或功能。

1.4配置内核(进入到可视化界面去)

配置文件千人千面,所以我们将一个官方的配置文件拷贝到.config中,在其中根据需要进行修改。
(1)首先在顶层目录中将默认配置拷贝到.condig中

cp config_mini2440_ td35.config 第一次编译内核

{

这个命令通常用于配置文件的替换或备份,特别是在嵌入式系统或Linux内核编译环境中,.config 文件经常用于存储内核配置选项。例如,如果你正在为基于ARM Cortex-A8的Mini2330开发板编译Linux内核,你可能会使用特定于该开发板的配置文件(如 config_mini2330_td35)作为起点,并将其重命名为 .config,以便内核编译系统能够识别和使用这些配置选项。

在执行这个命令之后,当前目录下的 config_mini2330_td35 文件将不再存在(因为它已经被复制并重命名为 .config),而 .config 文件将包含原 config_mini2330_td35 文件的全部内容。如果 .config 文件已经存在,它将被新内容覆盖,除非你在 cp 命令中使用了额外的选项(如 -i,它会在覆盖前询问用户

}

(2)在进入到可视化配置界面中

make  menuconfig  //可视化配置菜单——内核活地图

进入到该图形界面后,进行内核配置。配置时,大部分都是使用其默认选项,小部分才根据需要进行选择。

其中,每一个配置选项有三种选择,它们的含义如下:

<*>或[*]:表示将该功能编译进内核

[ ]:表示不将该功能编译进内核

[M]:表示将该功能编译成在需要时动态插入到内核的代码

用户根据需要进行配置。

(3)生成一个 U-Boot 兼容的映像文件(uImage)

make uImage 

显示 ready则完成

如果出现

注释顶层目录中的 kernel/timeconst.pl 第373行

(4)将这个uImage文件拷贝到tftpboot中

(5)打开开发板sudo minicom 倒数之前按回车 进入到SMDK2410中

启动过程中 如果重新出现倒数界面

(6)验证

添加文件 如果出现在开发板那边即已经完成。

关于Image文件

二、在内核中添加新文件。

(1)在顶层目录下的/drivers/char/中新建一个文件夹kcf(名字自定义)

(2)进入到新建的目录中

(3)写入一个Kconfig

简单的写入

(4)这样写完之后也不能在menuconfig中显示新创的选项,
          需要在char目录中的Kconfig中引用

(5)在menuconfig中可以找到相应的选项
(输入“/”则可开始寻找)

三、操作实例:

step1

step2 修改该目录下的makefile

step3:

在该目录下的Kconfig中加入配置选项

step4
回到/Linux-2.6.32.2 中 make uImage将

编译好的uImage在/Linux-2.6.32.2/arch/arm/boot中 

将其复制到/home/linux/tftpboot中并用chmod 777 uImage给权限

在可视化界面中打开选项

再进到minicom中 tftp 0x30008000 uImage下载到开发板中

运行后可以显示:

再可视化目录中关闭选项后重复上述编译uImage的操作

####################会删除掉如下图

四、驱动介绍

在Linux内核开发中,驱动(Driver)是一种特殊的软件,它充当硬件设备和操作系统之间的接口。驱动程序允许操作系统控制和管理硬件设备,包括初始化设备、发送数据到设备、从设备接收数据以及检测和处理硬件错误等


 

顶层:应用程序(APP)

  • APP open(led): 这表示一个用户空间的应用程序正在尝试打开一个名为“led”的设备。在Linux中,设备通常被当作文件来处理,因此使用open系统调用来访问它们。

系统调用层(SYS)

  • SYS open(led) - inode: 当应用程序执行open(led)时,这个请求会通过系统调用接口(如syscall)传递给内核。内核中的sys_open函数(或其他等效函数,具体取决于内核版本和架构)会处理这个请求。inode是文件系统中的一个关键数据结构,用于存储文件的元数据(如权限、大小、创建时间等),但在这里它也被用来表示设备文件(如LED设备)的元数据。

内核层(Kernel)

  • Kernel: 这是操作系统的核心部分,负责处理系统调用、管理硬件资源、提供进程调度等功能。
  • Struct inode LED 1: 在内核中,与“led”设备相关的数据被存储在一个inode结构体中,这里特别标记为“LED 1”,可能表示这是第一个LED设备或某个特定编号的LED。这个结构体包含了设备所需的所有信息,以便内核能够与之交互。
  • read() write() close(): 这些是内核中常见的文件操作函数,尽管对于LED这样的设备,readwrite操作可能不直接读取或写入数据(因为LED主要是一个输出设备),但它们可能被用来控制LED的状态(如打开、关闭、改变亮度等)。close函数用于关闭设备文件,释放相关资源。

驱动程序层

  • LED DRI, KEY_DRI, LCD_DRI: 这些是设备驱动程序的缩写,分别代表LED驱动、键盘驱动和LCD显示驱动。在Linux中,每个硬件设备都需要一个对应的驱动程序来与内核交互。这里的“DRI”可能指的是某种类型的驱动接口或规范,但更常见的是直接看到如“led_driver”这样的命名。
  • 在这个示意图中,LED DRI是重点,它负责处理与LED设备相关的所有操作,如响应openreadwriteclose系统调用,以及直接与硬件通信以控制LED的状态。

底层:硬件

  • LED, KEY, LCD: 这些是实际的硬件设备,分别代表LED灯、键盘和LCD显示屏。在这个上下文中,LED是重点,它根据驱动程序发出的指令来打开、关闭或改变其状态。

综上所述,这张图片展示了Linux系统中应用程序如何通过系统调用与内核交互,进而通过驱动程序控制硬件设备的基本流程。尽管它有所简化,但它很好地概括了Linux驱动程序的工作原理。


五、编写自己的驱动程序

1、我们需要把驱动程序放在

写的是字符数据驱动

Makefile和Kconfig文件改好 make uImage后生成下图所示才算完成

2、关于module_init(demo_init);和module_exit函数

module_init 宏

module_init 是一个特殊的宏,用于指定当模块被加载到内核时应该调用的初始化函数。它通常位于模块源文件的末尾,以确保在编译时能够正确地与内核模块机制相链接。module_init 宏接受一个函数指针作为参数,这个函数就是模块的初始化函数。

module_exit 宏

与 module_init 类似,module_exit 宏用于指定当模块从内核卸载时应该调用的清理函数。它也接受一个函数指针作为参数,这个函数负责执行必要的清理工作。

在内核源代码中,module_exit 宏的定义与 module_init 类似,但它是用于模块卸载的上下文中

 3、测试函数能否成功

类似前面几项的操作

选中menu选项后

不选择选项后井号消失。

4、关于设备号

设备号有32位;
主设备号:设备类型
次设备号:  同类设备的编号

必须要手动创建设备节点

mknod /dev/demo c 255  0

/dev/demo   设备节点号

255  主设备号

0     次设备号

2、关于操作方法(opration)

有一个非常重要的结构体

strruct  file_operation

{

      '''''''''''

}

其中实际上是一系列的函数指针

ssize是文件偏移量。

头文件<linux/fs.h>

             在Linux内核模块中定义一个简单的字符设备驱动,并为其实现了基本的文件操作函数:打开(open)、读取(read)、写入(write)和关闭(close)。不过,需要注意的是,在Linux内核中,close 函数通常被命名为 release 或 release_file,因为标准的文件操作结构体 file_operations 中并没有直接命名为 close 的成员。此外,printk 函数用于在内核日志中打印信息,这对于调试和跟踪内核模块的行为非常有用。

int open (struct inode * inode, struct file * file)
{
	printk("demo open ...\n");
	return 0;
}

ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	printk("demo read ...\n");
	return 0;
}

ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	printk("demo write ...\n");
	return 0;
}

int close (struct inode * inode, struct file * file)
{
	printk("demo close ...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.release = close    //给方法赋值
};

 3、关于绑定设备号跟操作方法

达到的效果:

需要使用一个结构体

将设备号码和操作方法都放在结构体中

结构体中包含链表的节点。

在代码中实现


4.向内核注册驱动节点。

需要使用到注册函数

register_chrdev_region(dev,1,DEV_NAME)

(需要在上面加一个 

#define DEV_NAME "demo")

注册代码的具体伪代码

static struct cdev cdev;
static dev_t dev;

static int __init demo_init(void)
{
	dev = MKDEV(MAJOR_NUM, MINOR_NUM);

	cdev_init(&cdev, &fops);

	cdev_add(&cdev, dev, DEV_NUM);

	register_chrdev_region(dev, DEV_NUM, DEV_NAME);

	printk("demo_init  ###############################\n");

	return 0;
}

5、卸载函数做与init()逆操作即可

static void __exit demo_exit(void)
{
	unregister_chrdev_region(dev, DEV_NUM);
	cdev_del(&cdev);
	printk("demo_exit  ###############################\n");
}

6,需要写一个应用程序

在/home/linux/nfs/rootfs中编写APP程序

运行:

只这样做开发板方面还是还是会找不到文件。

是因为设备中还没有设备节点

在arm端运行后需要手动添加节点

总体代码:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/cdev.h>

#define MAJOR_NUM  255
#define MINOR_NUM  0
#define DEV_NAME "demo"

static int open(struct inode * node, struct file * file)
{
	printk("demo open ...\n");
	return 0;
}

static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * loff)
{
	printk("demo read ...\n");
	return 0;
}

static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * loff)
{
	printk("demo write ...\n");
	return 0;
}

static int close(struct inode * node, struct file * file)
{
	printk("demo close ...\n");
	return 0;
}

static dev_t dev_num;
static struct cdev dev;
static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.release = close
};

static int __init demo_init(void)
{
	int ret = 0;
	dev_num = MKDEV(MAJOR_NUM, MINOR_NUM); ;//(MAJOR_NUM << 20) | MINOR_NUM;

	ret = cdev_add(&dev, dev_num, 1);
	if(ret < 0)
		goto err1;

	cdev_init(&dev, &fops);

	ret = register_chrdev_region(dev_num, 1, DEV_NAME);
	if(ret < 0)
		goto err2;

	printk("demo_init   ###############################\n");
	return 0;

err1:
	cdev_del(&dev);
	printk("demo cdev_add failed ret = %d\n", ret);
	return ret;

err2:
	unregister_chrdev_region(dev_num, 1);
	cdev_del(&dev);
	printk("demo register_chrdev_region failed ret = %d\n", ret);
	return ret;
}

static void __exit demo_exit(void)
{
	unregister_chrdev_region(dev_num, 1);
	cdev_del(&dev);
	printk("demo_exit   ###############################\n");
}

module_init(demo_init);
module_exit(demo_exit);


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

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

相关文章

linux应用编程--网络编程(socket编程基础)

介绍&#xff1a;套接字&#xff08;socket&#xff09;是 Linux 下的一种进程间通信机制&#xff08;socket IPC&#xff09;&#xff0c;在前面的内容中已经给大家提到过&#xff0c; 使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信&#xff08;网络通信&#…

Python(PyTorch)多语言图像感知质量指标算法

&#x1f3af;要点 &#x1f3af;算法实现&#xff1a;&#x1f58a;PyTorch单尺度和多尺度质量指标算法 | &#x1f58a;C单尺度质量指标算法 | &#x1f58a;Rust多尺度质量指标算法 | &#x1f58a;LabVIEW单尺度质量指标算法 | &#x1f58a;MATLAB单尺度质量指标算法 | &…

TCP+UDP通信

一、UDP协议 1.1、recvfrom() 参数说明 int sockfd, //socket 的fd void *buf, // 保存数据的一块空间的地址 size_t len, //这块空间的大小 int flags,// 0 默认的接收方式 -----阻塞方式 默认行为是阻塞 MSG_DONTWAIT 不阻塞方式&#xff0c;用他的话代表读的时候是非…

宠物掉毛、有异味怎么办?怎么选择宠物空气净化器?

每当我和朋友提起我家养猫养狗之后&#xff0c;不少朋友总会带着好奇与担忧的表情&#xff0c;半开玩笑地说&#xff1a;“你家里岂不是充满了‘特别’的味道&#xff1f;”这也不怪她们会有这种印象&#xff0c;因为大部分养宠家庭可能都会遇到浮毛满天飞的情况&#xff0c;但…

深入探讨SD NAND的SD模式与SPI模式初始化

在嵌入式系统和存储解决方案中&#xff0c;SD NAND的广泛应用是显而易见的。CS创世推出的SD NAND支持SD模式和SPI模式&#xff0c;这两种模式在功能和实现上各有优劣。在本文中&#xff0c;我们将深入探讨这两种模式的初始化过程&#xff0c;并比较它们在不同应用场景下的优劣&…

什么是上网行为管理呢?【上网行为管理系统功能介绍 】

小编想跟大家介绍上网行为管理系统的功能&#xff0c;可以提高工作效率和安全性。如果你也和部分管理者一样&#xff0c;经常为网络管理头疼&#xff0c;不妨看看小编解析的上网行为管理功能&#xff0c;会让眼前一亮哦&#xff01;上网行管控 应用场景&#xff1a;过滤非法网站…

Tomcat类加载机制详解

1.Tomcat类加载机制详解 1.1 JVM类加载器 Java中有 3 个类加载器&#xff0c;另外你也可以自定义类加载器 引导&#xff08;启动&#xff09;类加载器&#xff1a;负责加载支撑JVM运行的位于JRE的lib目录下的核心类库&#xff0c;比如rt.jar、charsets.jar等扩展类加载器&am…

流量掘金付费进群源码,最新无人直播变现玩法

外面1800流量掘金付费进群搭建 最新无人直播变现玩法 流量掘金付费进群网站源码 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89662670 更多资源下载&#xff1a;关注我。

消化学科的领军人物陈烨教授在会议上作了《幽门螺杆菌的规范检测与质控》的专题报告

由广东省药学会主办的“第十九届消化疾病诊疗会暨胃肠疾病药物临床研究交流会”于2024年8月8日-9日在广东省深圳市召开。陈烨教授&#xff0c;作为消化学科的领军人物、中华医学会消化病学分会的常务委员&#xff0c;以及全国幽门螺杆菌学组的组长&#xff0c;在会议上作了《幽…

用爬虫玩转石墨文档细解

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 石墨文档是一款受欢迎的在线协作工具&#xff0c;它允许多人实时编辑和共享文档。通过爬虫技术&#xff0c;我们可以自动化地获取石墨文档中的内容&#xff0c;进行数据分析或备份。不过&#xff0c;在使用爬虫技术时&a…

Nmap扫描六种端口状态介绍

传统意义上的端口状态只有 open 或 close 两种。网络发现扫描最常用的工具是Nmap&#xff0c;可以提供更细分的端口状态&#xff0c;共计六种&#xff0c;分别为open, closed, filtered, unfiltered, open|filtered, or closed|filtered。 1. 端口状态介绍 开放的&#xff08;o…

艾多美携手三星SDS,共筑物流优化与数字化转型新篇章!

8月20日下午&#xff0c;艾多美公司董事长朴炳宽受邀出席了由山东省人民政府、韩国产业通商资源部联合主办的“山东省—韩国经贸合作交流会”。在此次盛会上&#xff0c;艾多美中国与全球领先的IT解决方案提供商三星SDS达成了具有重要里程碑意义的战略合作&#xff0c;双方将共…

安防监控EasyCVR视频监控汇聚管理平台登录1分钟之后自动退出是什么原因?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。该平台不仅具备视频资源管理、设备管理、用户管理、网络管理和安全管理等功能&#xff0c;还支持多种主流标准协议&#xff0c;如GB28181、RTSP/Onvif、RTMP、部标JT808、GA/T 1400协…

docker基本环境搭建

前面在虚拟机centos中搭建的fastdfs和minio分布式文件存储服务都是手动编译安装的&#xff0c;为了方便后续学习&#xff0c;本地开发环境的中间件服务部署&#xff0c;我们将交给docker来部署。下面先进行docker环境搭建。 后续相关教程&#xff08;待更新&#xff09;&#…

QT中通过TCP协议多线程的文件传输(客户端)

首先&#xff0c;新建一个项目&#xff0c;我命名为了SendFileClient 首先我们要在pro文件中 代码第一行加入network的后缀 一、窗口搭建 如图所示&#xff0c;在第一个QWidget中让客户端输入IP&#xff0c;端口号 连接服务器 第二个Qwidget 设置一个LineEdit,供客户端选择要…

二维中,若直线上两点q1和q2,输入一个点P1,求P1在直线上的垂点

一、计算过程 在二维空间中&#xff0c;若给定直线上两点Q1和Q2以及一个点P1&#xff0c;要求出点P1在直线上的垂点&#xff0c;可以通过以下步骤进行&#xff1a; ‌1、判断点P1是否在直线q1-q2上‌&#xff1a; 首先&#xff0c;需要判断点P1是否位于直线Q1-Q2上。这可以通过…

【Linux】实现三个迷你小程序(倒计时,旋转指针,进度条)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:Linux ⚙️操作环境:Xshell (操作系统:CentOS 7.9 64位) 目录 &#x1f4cc;倒计时小程序 &#x1f38f;项目效果展示 &#x1f38f;项目实现思路 &#x1f38f;项目完整代码 &#x1f4cc;旋转指针小程序 &#x…

视频监控接入汇聚平台如何根据客户要求定制资源树结构和资源的任意排序

目录 一、需求描述 1.1视频监控资源树 1.2客户要求 二、市场上产品常用处理方法 2.1 常用处理方法 &#xff08;1&#xff09;按笔画数排序 &#xff08;2&#xff09;按拼音排序 &#xff08;3&#xff09;按字典序排序 &#xff08;4&#xff09;按首字母排序 2.2 …

使用预训练的 ONNX 格式的目标检测模型(基于 YOLOv8n-pose)姿态监测

具体步骤如下&#xff1a; 加载图像&#xff1a; 从指定路径读取一张图像&#xff08;这里假设图像名为bus.jpg&#xff09;。将图像从 BGR 颜色空间转换为 RGB 颜色空间。 图像预处理&#xff1a; 计算图像的高度、宽度&#xff0c;并确定其中的最大值作为新图像的边长。创建一…

C语言新手小白详细教程:冒泡排序

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明冒泡排序简介冒泡排序代码 开篇说明 本文我们来介绍冒…