Linux 设备分类详解:字符设备、块设备与网络设备解析

引言

在嵌入式 Linux 领域,设备驱动程序是操作系统与硬件之间的桥梁。Linux 设备按照不同的访问方式和特性可以划分为字符设备、块设备和网络设备。理解它们的分类及特性,对于开发 Linux 设备驱动和操作底层硬件至关重要。本文将深入解析这三大类设备的特点、访问方式及其在 Linux 内核中的实现方式。


1. Linux 设备的分类

Linux 设备根据数据的读写方式和访问特性,主要可以分为以下三类:

  • 字符设备(Character Device)
  • 块设备(Block Device)
  • 网络设备(Network Device)

这些设备分别对应不同的驱动模型和访问方式,下面分别进行详细分析。


2. 字符设备(Character Device)

2.1 概述

字符设备是一种 按字节流方式 访问的设备,数据是 线性顺序 读写的,没有统一的缓存管理机制。例如,串口(UART)、I²C 设备、GPIO、键盘、显示器等,都属于字符设备。

2.2 访问方式

字符设备通常通过文件操作接口(openreadwriteioctl 等)进行访问。典型的访问方式如下:

c

复制

int fd = open("/dev/ttyS0", O_RDWR);
write(fd, "Hello", 5);
read(fd, buffer, sizeof(buffer));
close(fd);

2.3 驱动实现

字符设备驱动在 Linux 内核中通常使用 cdev 结构体进行管理。一个基本的字符设备驱动框架如下:

c

复制

static struct cdev my_cdev;
static dev_t dev_num;static struct file_operations my_fops = {.owner = THIS_MODULE,.read = my_read,.write = my_write,.open = my_open,.release = my_release,
};static int __init my_driver_init(void) {alloc_chrdev_region(&dev_num, 0, 1, "my_device");cdev_init(&my_cdev, &my_fops);cdev_add(&my_cdev, dev_num, 1);return 0;
}static void __exit my_driver_exit(void) {cdev_del(&my_cdev);unregister_chrdev_region(dev_num, 1);
}module_init(my_driver_init);
module_exit(my_driver_exit);

3. 块设备(Block Device)

3.1 概述

块设备是一种 以块(block)为单位 进行存取的设备,通常具有 缓冲区(Buffer)或者缓存(Cache) 机制,支持随机访问。典型的块设备包括 硬盘(HDD/SSD)、SD 卡、U 盘、eMMC 等

3.2 访问方式

块设备通常通过文件系统进行访问,用户可以使用 openreadwrite 进行文件操作,或者通过 dd 命令直接访问设备。例如:

sh

复制

dd if=/dev/sda of=backup.img bs=1M count=100

3.3 驱动实现

块设备驱动通常基于 request_queue 进行数据调度,基本的块设备驱动框架如下:

c

复制

static struct request_queue *my_queue;
static struct gendisk *my_disk;static void my_request_fn(struct request_queue *q) {struct request *req;while ((req = blk_fetch_request(q)) != NULL) {// 处理请求__blk_end_request_all(req, 0);}
}static int __init my_block_driver_init(void) {my_queue = blk_init_queue(my_request_fn, NULL);my_disk = alloc_disk(1);my_disk->queue = my_queue;snprintf(my_disk->disk_name, 32, "my_block");add_disk(my_disk);return 0;
}static void __exit my_block_driver_exit(void) {del_gendisk(my_disk);put_disk(my_disk);blk_cleanup_queue(my_queue);
}module_init(my_block_driver_init);
module_exit(my_block_driver_exit);

4. 网络设备(Network Device)

4.1 概述

网络设备用于数据包(Packet)的收发,而不是以字节或块为单位进行访问。典型的网络设备包括 以太网网卡(Ethernet)、Wi-Fi 模块、LoRa、蓝牙(Bluetooth)等

4.2 访问方式

网络设备通常不会通过 /dev 文件访问,而是通过 socket(套接字) 进行通信。例如,使用 socket 编程来创建 TCP 连接:

c

复制

int sock = socket(AF_INET, SOCK_STREAM, 0);
connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
send(sock, "Hello", 5, 0);
recv(sock, buffer, sizeof(buffer), 0);
close(sock);

4.3 驱动实现

网络设备驱动基于 net_device 结构体,并使用 netif_rx() 处理接收数据包。基本的网络设备驱动框架如下:

c

复制

static struct net_device *my_netdev;static int my_net_open(struct net_device *dev) {netif_start_queue(dev);return 0;
}static int my_net_stop(struct net_device *dev) {netif_stop_queue(dev);return 0;
}static netdev_tx_t my_net_xmit(struct sk_buff *skb, struct net_device *dev) {dev_kfree_skb(skb);return NETDEV_TX_OK;
}static struct net_device_ops my_netdev_ops = {.ndo_open = my_net_open,.ndo_stop = my_net_stop,.ndo_start_xmit = my_net_xmit,
};static int __init my_net_driver_init(void) {my_netdev = alloc_netdev(0, "mynet%d", NET_NAME_UNKNOWN, ether_setup);my_netdev->netdev_ops = &my_netdev_ops;register_netdev(my_netdev);return 0;
}static void __exit my_net_driver_exit(void) {unregister_netdev(my_netdev);free_netdev(my_netdev);
}module_init(my_net_driver_init);
module_exit(my_net_driver_exit);

5. 设备号与 /dev 目录

Linux 通过 主设备号(Major Number)次设备号(Minor Number) 标识设备。主设备号标识设备类型,次设备号标识具体的设备实例。例如:

sh

复制

ls -l /dev/
crw-rw---- 1 root dialout 4, 64 /dev/ttyS0  # 字符设备
brw-rw---- 1 root disk    8, 0  /dev/sda    # 块设备

设备号的分配可通过 mknod 命令手动创建:

sh

复制

mknod /dev/my_device c 240 0  # 创建字符设备
mknod /dev/my_block b 241 0   # 创建块设备

总结

本文详细介绍了 Linux 设备的三大类别——字符设备、块设备和网络设备,并解析了它们的访问方式、驱动模型和 Linux 内核中的实现方式。在嵌入式 Linux 开发中,理解设备分类及其驱动原理是开发高性能、稳定的系统的基础。希望本文能为你的 Linux 设备驱动开发提供有价值的参考!

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

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

相关文章

6.4 模拟专题:LeetCode1419.数青蛙

1.题目链接:数青蛙 - LeetCode 2.题目描述 给定一个字符串 croakOfFrogs,表示青蛙的鸣叫声序列。每个青蛙必须按顺序发出完整的 “croak” 字符,且多只青蛙可以同时鸣叫。要求计算最少需要多少只青蛙才能完成该字符串,若无法完成…

Linux 搭建dns主域解析,和反向解析

#!/bin/bash # DNS主域名服务 # user li 20250325# 检查当前用户是否为root用户 # 因为配置DNS服务通常需要较高的权限,只有root用户才能进行一些关键操作 if [ "$USER" ! "root" ]; then# 如果不是root用户,输出错误信息echo "…

Leetcode 二进制求和

java solution class Solution {public String addBinary(String a, String b) {StringBuilder result new StringBuilder();//首先设置2个指针, 从右往左处理int i a.length() - 1;int j b.length() - 1;int carry 0; //设置进位标志位//从2个字符串的末尾向前遍历while(…

【NLP 49、提示工程 prompt engineering】

目录 一、基本介绍 语言模型生成文本的基本特点 提示工程 prompt engineering 提示工程的优势 使用注意事项 ① 安全问题 ② 可信度问题 ③ 时效性与专业性 二、应用场景 能 ≠ 适合 应用场景 —— 百科知识 应用场景 —— 写文案 应用场景 —— 解释 / 编写…

【NLP 43、文本生成任务】

目录 一、生成式任务 二、seq2seq任务 1.模型结构 2.工作原理 3.局限性 三、自回归语言模型训练 Decoder only 四、自回归模型结构:文本生成任务 —— Embedding LSTM 代码示例 🚀 数据文件 代码流程 Ⅰ、模型初始化 Ⅱ、前向计算 代码运行流程 Ⅲ、加载…

vscode 通过Remote-ssh远程连接服务器报错 could not establish connection to ubuntu

vscode 通过Remote-ssh插件远程连接服务器报错 could not establish connection to ubuntu,并且出现下面的错误打印: [21:00:57.307] Log Level: 2 [21:00:57.350] SSH Resolver called for "ssh-remoteubuntu", attempt 1 [21:00:57.359] r…

Linux之编辑器vim命令

vi/vim命令: 终端下编辑文件的首选工具,号称编辑器之神 基本上分为三种模式,分别是 命令模式(command mode)>输入vi的命令和快捷键,默认打开文件的时候的模式插入模式(insert mode&#x…

第一天学爬虫

阅读提示:我今天才开始尝试爬虫,写的不好请见谅。 一、准备工具 requests库:发送HTTP请求并获取网页内容。BeautifulSoup库:解析HTML页面并提取数据。pandas库:保存抓取到的数据到CSV文件中。 二、爬取步骤 发送请求…

MySQL实战(尚硅谷)

要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…

windows下安装sublime

sublime4 alpha 4098 版本 下载 可以根据待破解的版本选择下载 https://www.sublimetext.com/dev crack alpha4098 的licence 在----- BEGIN LICENSE ----- TwitterInc 200 User License EA7E-890007 1D77F72E 390CDD93 4DCBA022 FAF60790 61AA12C0 A37081C5 D0316412 4584D…

激光线检测算法的FPGA实现

激光线检测算法的FPGA实现 1. 常见的激光线检测算法 激光线检测中常用的三种算法 MAX(最大值法)、THRESH(阈值法)、COG(灰度重心法) 分别具有以下特点和工作原理: 1.1 MAX(最大值法…

小样本微调大模型

一、环境搭建 conda create -n dseek python=3.10 conda activate dseek pip install bitsandbytes Pip install numpy python -m pip install --upgrade pip setuptools wheel 安装cuda,torch,Unsloth, huggingface,wandb等,见前述章节; 微调服务器配置:单机笔记本显卡4…

深入理解指针(2)(C语言版)

文章目录 前言一、数组名的理解二、使用指针访问数组三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组七、指针数组模拟二维数组总结 前言 在上一篇文章中,我们初步了解了指针的基本概念和用法。今天,我们将继续深入探索指针在数组、函数传…

高效内存管理:x86-64架构中的分页机制

在 x86-64 架构的世界里,内存分页机制扮演着举足轻重的角色,它就像是一座桥梁,连接着虚拟地址与物理地址。简单来说,内存分页机制就是将线性地址(也就是虚拟地址)切分成一个个固定大小的页,并把…

统一开放世界与开放词汇检测:YOLO-UniOW无需增量学习的高效通用开放世界目标检测框架

目录 一、摘要 二、引言 三、相关工作 开放词汇对象检测 开放世界目标检测 参数高效学习 四、高效通用的开放世界目标检测 问题定义 高效的自适应决策学习 开放世界通配符学习 五、Coovally AI模型训练与应用平台 六、实验 数据集 评价指标 实施细节 定量结果 …

fileinclude

##解题思路 场景首页没有什么提示,只有个flag在flag.php中,而且需要更改language,还有个index.php的路径,先记住它 习惯性查看源代码,得到了题目真正的内容,关键在于lan变量读取我们传入的Cookie值中的lang…

链表-LeetCode

这里写目录标题 1 排序链表1.1 插入法 O(n)1.2 归并排序 1 排序链表 1.1 插入法 O(n) /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullpt…

计算机网络基础:WiFi 与蓝牙的原理与应用

计算机网络基础:WiFi 与蓝牙的原理与应用 一、前言二、WiFi 原理2.1 概述2.2 工作频段2.2.1 2.4GHz 频段2.2.2 5GHz 频段2.3 调制技术2.3.1 正交频分复用(OFDM)2.3.2 直接序列扩频(DSSS)2.4 通信协议2.5 网络架构2.5.1 独立基本服务集(IBSS)2.5.2 基础服务集(BSS)2.5.…

深入解析 Java 类加载机制及双亲委派模型

🔍 Java的类加载机制是确保应用程序正确运行的基础,特别是双亲委派模型,它通过父类加载器逐层加载类,避免冲突和重复加载。但在某些特殊场景下,破坏双亲委派模型会带来意想不到的效果。本文将深入解析Java类加载机制、…

【数据可视化艺术·进阶篇】热力图探秘:用色彩演绎场馆和景区的人流奥秘

假期出游,你是不是也遇到过这样的状况:想去的热门景点,放眼望去全是攒动的人头,根本没法好好欣赏风景;而景区里一些小众角落,却冷冷清清,鲜有人至。还有在轨道交通枢纽、大型体育场这些地方&…