嵌入式Linux驱动开发的基本知识(驱动程序的本质、常见的设备类型、设备号的本质理解、设备实例的注册过程)
基本概念之什么是驱动程序()?
驱动程序本质上是代码逻辑的集合,通常用于管理、驱动多个设备实例。某个设备要想使用驱动程序,需要实例化相应的驱动程序的结构体,并在系统中注册,获得主设备号、次设备号,并将相应的结构体、变量、函数等进行绑定。
这里重点要理解驱动程序只是代码逻辑的集体,并不等同于设备,驱动程序本身并没有主设备号、次设备号等,主设备号、次设备号是在注册设备实例时才有的概念。
Linux系统中常见的设备类型有哪些?
在 Linux 系统中,设备可以分为不同的类型,主要根据它们的工作方式、接口和功能进行分类。以下是 Linux 系统中常见的设备类型:
1. 字符设备 (Character Devices)
字符设备是按字符流方式进行数据传输的设备,每次读写都涉及一个字符或字节的数据。例如:
- 终端设备 (
tty
): 用于与用户交互的设备,如串口终端、虚拟终端。 - 串口设备 (
ttyS
): 串口通信设备,如/dev/ttyS0
。 - 键盘 (
kbd
): 连接的键盘设备。 - 鼠标 (
mouse
): 连接的鼠标设备。 - 伪设备:例如
/dev/null
、/dev/random
、/dev/zero
等,这些设备没有硬件实现,提供特殊的功能。
特点:
- 按字符流进行读写,每次读写一个字符(或字节)。
- 通过字符设备文件(如
/dev/ttyS0
)与应用程序交互。
2. 块设备 (Block Devices)
块设备是按块(通常是 512 字节或更大的单位)进行数据传输的设备。它们支持随机访问和缓存功能。例如:
- 硬盘 (
/dev/sda
,/dev/sdb
等): 计算机的主要存储设备。 - 固态硬盘 (SSD) (
/dev/sda1
,/dev/nvme0n1
): 新型存储设备,提供更高的读写速度。 - 光盘驱动器 (
/dev/cdrom
): 用于读取光盘数据的设备。 - USB 存储设备 (
/dev/sdb1
,/dev/sdc
): 连接到系统的 USB 存储设备。 - 虚拟磁盘 (
/dev/loop0
): 用于挂载虚拟磁盘映像的设备。
特点:
- 支持随机读写访问,通常用于文件系统的挂载。
- 设备文件通常位于
/dev/
目录下。
3. 网络设备 (Network Devices)
网络设备是用于网络通信的设备。它们支持网络接口的连接和数据传输。例如:
- 以太网卡 (
eth0
,eth1
): 网络接口,用于连接有线网络。 - 无线网卡 (
wlan0
,wlan1
): 用于连接无线网络。 - 虚拟网络设备 (
lo
): 回环接口,用于与本机进行网络通信。
特点:
- 用于实现计算机与外部网络的通信。
- 可以是有线或无线设备,支持 TCP/IP 等协议。
4. 输入设备 (Input Devices)
输入设备是用于将用户输入传递给计算机的设备。它们通常使用字符设备接口。例如:
- 鼠标 (
/dev/input/mice
): 用于提供用户的指针控制输入。 - 键盘 (
/dev/input/event0
): 用于接收用户的键盘输入。 - 触摸屏 (
/dev/input/eventX
): 用于接收触摸操作。
特点:
- 输入设备通常通过
/dev/input/
目录进行管理,使用字符设备接口。 - 输入事件通过设备文件传递给应用程序。
5. 虚拟设备 (Virtual Devices)
虚拟设备并不对应于物理硬件设备,而是通过软件实现的设备。例如:
- 虚拟终端 (
/dev/tty0
,/dev/tty1
): 提供与系统交互的虚拟控制台。 - 内存设备 (
/dev/mem
): 用于访问物理内存的虚拟设备。 - 随机数设备 (
/dev/random
,/dev/urandom
): 用于生成随机数的设备。
特点:
- 不依赖于硬件,而是由内核或驱动程序模拟。
- 提供系统管理或特殊功能。
6. 特殊设备 (Special Devices)
这些设备通常与硬件密切相关,但它们的作用和用法比较特殊。例如:
- 时钟设备 (
/dev/rtc
): 实时钟设备,用于管理系统时间。 - 伪设备 (
/dev/null
,/dev/zero
): 不涉及实际硬件,而是提供特殊功能,通常用于丢弃或生成数据。
7. USB 设备 (USB Devices)
USB 设备通过 USB 总线连接到计算机,涵盖了各种设备类型。例如:
- USB 存储设备 (
/dev/sda1
,/dev/sdb1
): 连接的 USB 存储。 - USB 摄像头 (
/dev/video0
): 通过 USB 接口连接的摄像头。 - USB 键盘和鼠标 (
/dev/input/eventX
): 连接的 USB 键盘和鼠标。
特点:
- 支持即插即用(plug-and-play),可以动态连接和断开。
8. 串口设备 (Serial Devices)
串口设备通过串行接口进行通信,通常用于较为传统的设备。例如:
- 串口终端 (
/dev/ttyS0
,/dev/ttyS1
): 传统的串口设备。 - 调制解调器 (
/dev/ttyUSB0
): 通过串口连接的调制解调器。
特点:
- 通过串行接口传输数据,通常用于低速数据通信。
9. SCSI 设备 (SCSI Devices)
SCSI 是一种广泛用于硬盘、光驱等设备的标准接口。例如:
- SCSI 硬盘 (
/dev/sda
,/dev/sdb
): SCSI 接口的硬盘设备。 - SCSI 光驱 (
/dev/sr0
): SCSI 接口的光驱设备。
特点:
- 高性能和扩展性,常用于服务器或工作站中。
- 支持多个设备共享同一总线。
10. PCI 设备 (PCI Devices)
PCI 总线上的设备用于高性能数据交换。例如:
- 显卡 (
/dev/video0
): 通过 PCI 接口连接的显卡设备。 - 网卡 (
/dev/eth0
): 通过 PCI 接口连接的网卡设备。 - 音频设备 (
/dev/snd/pcmC0D0p
): 通过 PCI 接口连接的音频设备。
特点:
- 高速数据传输,支持多种设备接口。
- 通过 PCI 总线连接多个设备。
11. I2C 和 SPI 设备 (I2C/SPI Devices)
I2C 和 SPI 是常用于嵌入式设备的通信协议,用于连接传感器、显示器等设备。例如:
- I2C 设备:通过 I2C 总线连接的设备,通常位于
/dev/i2c-X
。 - SPI 设备:通过 SPI 总线连接的设备,通常位于
/dev/spidevX.Y
。
特点:
- 用于短距离、高速的设备间通信。
- 主要用于嵌入式系统中的外围设备。
小结
在 Linux 系统中,设备类型非常丰富,主要包括字符设备、块设备、网络设备、输入设备、虚拟设备、USB 设备、串口设备等。每种设备都有不同的特点和用途,Linux 系统通过设备文件(通常位于 /dev/
目录下)来对这些设备进行管理和访问。
什么叫主设备号、次设备号?
主设备号、次设备号是Linux系统管理设备的一种结构,通常如果有多个设备,其驱动程序如果一样,那我们为其分配相同的主设备号,内核通过主设备号找到负责处理该设备的驱动程序。但它们的次设备号不同,通过不同的次设备号来区别它们。
当然如果设备类型相同,驱动程序也相同,你也可以为它们分配不同的主设备号,不过不建议这样做,这不符号Linux的基本设计原则。
不过也有特例,比如不同类型的设备有时会使用相同的驱动程序,但此时我们也会为它们分配不同的主设备号,以便在系统中区分。例如:块设备和字符设备通常使用不同的主设备号,即使它们底层可能由同一个驱动程序管理。
以字符设备为例,有了驱动程序后,为设备提供驱动程序并注册进系统的通常流程是怎么样的?
这里以字符设备为例。
这里以字符设备为例。
这里以字符设备为例。
注意:不同的设备类型有不同的对应下面各步骤的函数,但流程基本上是一样的。
注意:不同的设备类型有不同的对应下面各步骤的函数,但流程基本上是一样的。
注意:不同的设备类型有不同的对应下面各步骤的函数,但流程基本上是一样的。
第一步是调用函数alloc_chrdev_region()
获得可用的主设备号、次设备号。
示例代码如下:
dev_t dev;
ret = alloc_chrdev_region(&dev, 0, 3, "my_device");
函数alloc_chrdev_region()
的原型如下:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);
参数解释
-
dev
指向一个dev_t
类型的变量,用于保存分配的设备号(包含主设备号和次设备号)。成功分配后,可以通过MAJOR(dev)
和MINOR(dev)
宏提取主设备号和次设备的起始号。 -
firstminor
表示从哪个次设备号开始分配,一般设置为0
。 -
count
表示需要分配的连续次设备号的数量。如果你需要多个次设备号,可以指定这个值(通常为1
表示只分配一个设备号)。 -
name
为设备指定一个名字,这个名字主要用于调试时显示(比如/proc/devices
中会看到)。
alloc_chrdev_region
运行后,系统为我们将要注册的设备分配了:
-
一个主设备号
主设备号是系统在字符设备表中分配的唯一标识,用于将设备与驱动程序关联。 -
一个连续的次设备号范围
根据调用时指定的count
参数,系统分配了一段连续的次设备号范围。
这意味着,分配完成后:
- 我们的设备对应的设备号范围是:
(主设备号, 起始次设备号) 到 (主设备号, 起始次设备号 + count - 1)。 - 每个次设备号可以用来表示不同的设备实例,但它们共享相同的主设备号。
这里要特别注意:变量dev
中保存的是系统为我们分享的主设备号和次设备的超始号,而不是所有的次设备号哈。
第二步是调用函数cdev_init()将描述字符设备的核心结构体cdev的实例和核心结构体file_operations的实例绑定起来
示例代码如下:
static struct cdev my_cdev; // 定义字符设备结构体
static struct file_operations my_fops = {.owner = THIS_MODULE,.open = my_open,.read = my_read,.write = my_write,.release = my_close,
};/* 核心结构体cdev和结构体file_operations的绑定 */
cdev_init(&my_cdev, &my_fops);}
这里面涉及到两个重要的结构体,第一个是struct cdev
、另一个是 struct file_operations
,下面进行介绍:
struct cdev
是 Linux 内核中的一个核心结构,用于表示字符设备的相关信息。
定义(简化版)
struct cdev {struct kobject kobj; // 内核对象,用于 sysfs 集成const struct file_operations *ops; // 设备支持的操作集struct list_head list; // 用于链接到内核的 cdev 列表dev_t dev; // 设备号unsigned int count; // 管理的次设备号范围
};
从其简化版的定义中可以看出,结构体struct cdev
的一个实例就把设备需要的最重要的信息描述完了:
cdev结构体的成员kobj这里暂时不展开叙述。
cdev结构体的成员ops实际上就是上面说到的两个重要结构体中的另一个 struct file_operations
,它里面实际上就是设备能进行的具体行为的函数的集合,比如下面是一个 struct file_operations
简化版的定义:
PS:完整定义请参考我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/144905840
struct file_operations {struct module *owner; // 指向模块的指针,防止模块卸载时被调用loff_t (*llseek)(struct file *, loff_t, int);ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);int (*open)(struct inode *, struct file *);int (*release)(struct inode *, struct file *);long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);...
};
从中我们可以看出它里面就集合了read、write、open、release这些真正涉及对设备底层操作的成员函数,这些函数其实才是某个设备驱动程序的核心。
cdev结构体的成员list_head list是用于链接到内核的 cdev 列表,这里不展开叙述。
cdev结构体的成员dev中存储了它的一个实例管理的设备号,包括主设备号和次设备号的起始号。
cdev结构体的成员count存储了次设备号的个数,通过dev中记录的次设备号的起始号和个数值,便知道了次设备号的范围。
从上面的描述可以看出,cdev结构体的一个实例确实是把设备需要的最重要的信息描述完了。而调用函数就是将其实例与结构体 struct file_operations
的实例绑定起来,结构体 struct file_operations
的实例中存储了设备能进行的具体行为的函数,比如了read、write、open这些函数。
第三步是调用函数cdev_add()将描述字符设备的核心结构体cdev的实例和设备号绑定起来
第一步中已经获取到了可用的主设备号和次设备号,这里就需要将描述字符设备的核心结构体cdev的实例与第一步中获取到的主设备号和次设备号信息填充上。
示例代码如下:
static dev_t dev;static struct cdev my_cdev; // 定义字符设备结构体static struct file_operations my_fops = {.owner = THIS_MODULE,.open = my_open,.read = my_read,.write = my_write,.release = my_close,
};// 向系统申请可用的主设备号和次设备号
ret = alloc_chrdev_region(&dev, 0, 3, "my_device");/* 核心结构体cdev和结构体file_operations的绑定 */
cdev_init(&my_cdev, &my_fops);/* 核心结构体cdev中写入设备号以及次设备号的个数 */
cdev_add(&my_cdev, dev, 3);....
在第二步中已经给出了核心结构体cdev的简化版:
struct cdev {struct kobject kobj; // 内核对象,用于 sysfs 集成const struct file_operations *ops; // 设备支持的操作集struct list_head list; // 用于链接到内核的 cdev 列表dev_t dev; // 设备号unsigned int count; // 管理的次设备号范围
};
可见,这里面的第4个成员dev和第5个成员count便是设备号信息。所以这里需要调用函数cdev_add()
向这个设备核心结构体写入设备号信息。
第四步调用函数class_create()创建设备类
设备类是内核设备模型中的一个抽象,用于将功能相似的设备分组,以便统一管理这些设备的属性和行为。通过设备类,我们可以轻松地在 /sys/class/ 下创建一个目录,并将设备文件与该类关联。
…这里未完待续…
第五步是调用函数device_create()创建设备文件
一个设备实例对就一个具体的设备文件,所以如果同一个驱动程序实例对应多个设备,便需要调用多次函数device_create()。
示例代码如下:
dev_t dev;
int ret = alloc_chrdev_region(&dev, 0, 3, "my_device"); // 分配3个次设备号
if (ret < 0) {printk(KERN_ERR "Failed to allocate device number\n");return ret;
}// 创建 cdev 并注册
cdev_init(&my_cdev, &my_fops);
ret = cdev_add(&my_cdev, dev, 3);
if (ret < 0) {printk(KERN_ERR "Failed to add cdev\n");unregister_chrdev_region(dev, 3);return ret;
}// 创建设备文件(多个次设备号,调用多次 device_create)
for (int i = 0; i < 3; i++) {// 为每个次设备号创建设备文件device_create(my_class, NULL, MKDEV(MAJOR(dev), MINOR(dev) + i), NULL, "my_device%d", i);
}
函数device_create()的介绍如下:
函数原型
struct device *device_create(struct class *cls, struct device *parent, dev_t devt,const struct attribute_group **groups, const char *devname);
参数解析:
-
my_class
(struct class *):- 这是一个指向
class
结构体的指针,表示设备所属的类。类是 Linux 设备模型中的一个概念,用来将一组具有相似功能的设备分组在一起。设备类管理着设备对象的生命周期、sysfs 属性、设备文件的创建等。 - 在例子中,
my_class
是事先通过class_create()
创建的类,可能类似于:my_class = class_create(THIS_MODULE, "my_class");
class_create()
用来创建设备类,它是 Linux 驱动程序中组织设备的基础。
- 这是一个指向
-
NULL
(struct device *):- 这个参数指定父设备。如果设备没有父设备,可以传递
NULL
。大多数设备驱动程序的设备对象不需要父设备,所以这里通常会传递NULL
。 - 父设备用于设备层次结构的管理,例如树形结构的设备关联。如果不需要设备层次结构,可以将其设为
NULL
。
- 这个参数指定父设备。如果设备没有父设备,可以传递
-
dev
(dev_t):- 这是设备的主设备号和次设备号组成的
dev_t
类型值,表示具体的设备。 dev_t
是一个 32 位的值,其中低 20 位是次设备号,高 12 位是主设备号。例如,如果你用alloc_chrdev_region()
分配了一个设备号,dev
就是该设备号。
- 这是设备的主设备号和次设备号组成的
-
**
NULL
(const struct attribute_group groups):- 这个参数是一个指向
attribute_group
结构体指针的指针,代表了设备的sysfs
属性。如果不需要额外的sysfs
属性,可以传递NULL
。 - 在许多情况下,你可能不需要为设备创建额外的
sysfs
属性,尤其是简单的字符设备。因此,可以把它设为NULL
。
- 这个参数是一个指向
-
"my_device"
(const char *):- 这是设备文件的名称,即最终会出现在
/dev/
目录下的设备文件名。在这个例子中,设备文件的名称是"my_device%d"
,因此设备文件名为/dev/my_device0
、/dev/my_device1
、/dev/my_device2
。
- 这是设备文件的名称,即最终会出现在
返回值:
device_create()
返回一个指向创建的device
结构体的指针,该结构体表示内核中创建设备对象的元数据。- 如果函数调用失败,它返回
NULL
。
驱动程序编译好后通常是以模块的形式存在的,那么怎么加载这个模块?这个模块加载时从哪里执行?
请参看博文:
以一个实际例子来学习Linux驱动程序开发之“设备类”的相关知识 【搜索关键字“驱动模块加载代码”】
为什么驱动程序模块的C文件末尾要加上MODULE_LICENSE("GPL");
关于这个问题详细的介绍见我的另一篇博文
https://blog.csdn.net/wenhao_ir/article/details/144902881
驱动程序模块加载完成,完成设备实例化注册并创建了设备文件后,怎么调用设备?
这个最简单的例子例是之前的:
“IMX6ULL开发板基础实验:Framebuffer驱动程序的简单应用实例代码详细分析”
关键代码为:
int fb_fd = open("/dev/fb0", O_RDWR);
这句代码就是把设备打开了,然后利用获得的文件描述符就可以操作设备了。
相关文章:

嵌入式Linux驱动开发的基本知识(驱动程序的本质、常见的设备类型、设备号的本质理解、设备实例的注册过程)
基本概念之什么是驱动程序()? 驱动程序本质上是代码逻辑的集合,通常用于管理、驱动多个设备实例。某个设备要想使用驱动程序,需要实例化相应的驱动程序的结构体,并在系统中注册,获得主设备号、次设备号,并…...

爱死机第四季(秘密关卡)4KHDR国语字幕
通过网盘分享的文件:love_death_robot 链接: https://pan.baidu.com/s/1bG3Xtdopenil2O_y93hY_g?pwd8kib 提取码: 8kib...

kubelet状态错误报错
journalctl -xeu kubelet 执行后的日志如下: -- -- The process exit code is exited and its exit status is 1. Jan 02 14:20:06 iv-ydipyqxfr4wuxjsij0bd systemd[1]: kubelet.service: Failed with result exit-code. -- Subject: Unit failed -- Defined-By: system…...

<div>{{ $t(“collectionPlan“) }}</div> 中的$t是什么
$t是Vue I18n插件提供的一种方法,用于根据当前应用的语言环境来获取相应的翻译文本。 以下是一个简单的示例,展示如何在Vue I18n中定义消息: const i18n new VueI18n({locale: en, // 设置默认语言messages: {en: {collectionPlan: Collec…...

[C++刷题] 求回文素数
求回文素数 题目 素数回文数的个数 题目描述 求 11 11 11 到 n n n 之间(包括 n n n),既是素数又是回文数的整数有多少个。 输入格式 一个大于 11 11 11 小于 10000 10000 10000 的整数 n n n。 输出格式 11 11 11 到 n n n 之…...

SQLALchemy如何将SQL语句编译为特定数据库方言
最近在一个使用fastapitortoise-orm的项目中,需要将orm的语句编译成特定数据库方言,但是查询了官方文档及一些资料却找不到合适的方法论😔,于是乎我就把目光放到了sqlalchemy身上,东找西找给我找着了。话不多说&#x…...

[卫星遥感] 解密卫星目标跟踪:挑战与突破的深度剖析
目录 [卫星遥感] 解密卫星目标跟踪:挑战与突破的深度剖析 1. 卫星目标跟踪的核心挑战 1.1 目标的高速与不确定性 1.2 卫星传感器的局限性 1.3 数据处理与融合问题 1.4 大尺度与实时性要求 2. 当前卫星目标跟踪的主流技术 2.1 卡尔曼滤波(Kalman …...

I2C(一):存储器模式:stm32作为主机对AT24C02写读数据
存储器模式:在HAL库中,I2C有专门对存储器外设设置的库函数 I2C(一):存储器模式的使用 1、I2C轮询式写读AT24C02一页数据2、I2C轮询式写读AT24C02多页数据3、I2C中断式写读AT24C02一页数据4、I2C使用DMA式写读AT24C02一…...

scrapy 教程
Scrapy Tutorial In this tutorial, we’ll assume that Scrapy is already installed on your system. If that’s not the case, see Installation guide. We are going to scrape quotes.toscrape.com, a website that lists quotes from famous authors. This tutorial …...

2025元旦源码免费送
我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。 免费获取源码。 更多内容敬请期待。如有需要可…...

高级架构五 设计模式
一 设计模式七大原则 1.1. 设计模式目的 编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好的&#…...

RFID手持机与RFID工业平板在仓储物流管理系统中的选型
概述 随着物联网技术在仓储物流管理系统中的普及,RFID手持机与RFID工业平板作为基于RFID技术手持式读写器的两种重要终端设备形态,得到了广泛应用。尽管RFID手持机与RFID工业平板都具备读写 RFID标签的基本功能,使用场景较为类似,…...

IoC设计模式详解:控制反转的核心思想
前言:在软件开发中,设计模式是一种经过验证的、在特定场景下能有效解决问题的解决方案。控制反转(Inversion of Control,IoC) 作为一种设计模式,通过让程序的控制流和对象管理反转,从而使得代码…...

《云原生安全攻防》-- K8s安全配置:CIS安全基准与kube-bench工具
在本节课程中,我们来了解一下K8s集群的安全配置,通过对CIS安全基准和kube-bench工具的介绍,可以快速发现K8s集群中不符合最佳实践的配置项,及时进行修复,从而来提高集群的安全性。 在这个课程中,我们将学习…...

LINUX下载编译gtk
下载 选择自己合适的版本 GNOME / gtk GitLab 下载meson GNOME / gtk GitLab 编译 BUILD_DIRbuilddir INSTALL_DIR${HOME}/gtk-resultMESON_PATHpwd/meson-1.6.1/meson.py${MESON_PATH} setup \--prefix ${INSTALL_DIR} \${BUILD_DIR}cd builddir${MESON_PATH} compile…...

基于VSCode软件框架的RISC-V IDE MRS2正式上线发布
基于VSCode软件框架的RISC-V IDE MRS2正式上线发布 一、概述 MounRiver Studio Ⅱ(MRS2)为MounRiver Studio的换代版本,从V2.1开始,框架更换至更现代的VSCode,并深度定制开发。在工程管理、代码编辑、编译、调试等方面均兼容之前版本&#…...

AWS re:Invent 2024 - Dr. Werner Vogels 主题演讲
今年,我有幸亲临现场参加了所有的 keynote,每一场都让我感受到深深的震撼。无论是全新的功能发布,还是令人眼前一亮的新特性展示,每一场 keynote 都精彩纷呈,充满干货,值得反复学习和回味。 恰好ÿ…...

前端小案例——520表白信封
前言:我们在学习完了HTML和CSS之后,就会想着使用这两个东西去做一些小案例,不过又没有什么好的案例让我们去练手,本篇文章就提供里一个案例——520表白信封 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主…...

FPGA随记——过约束
什么是过约束? 从字面意思来看,所谓过约束是指约束过紧了。这个“过”体现在setup requirement变小了,对于单周期路径,这个值通常与时钟周期一致,但在过约束情况下,等效于这个值变小了,也就是时…...

如何利用云计算进行灾难恢复?
云计算环境下的灾难恢复实践指南 天有不测风云,企业的IT系统也一样,我见过太多因为没有做好灾备而吃大亏的案例。今天就和大家聊聊如何用云计算来做灾难恢复。 一个惊心动魄的真实案例:某电商平台的主数据中心因为市政施工不小心挖断了光纤…...

【华为OD-E卷 - 九宫格按键输入 100分(python、java、c++、js、c)】
【华为OD-E卷 - 九宫格按键输入 100分(python、java、c、js、c)】 题目 九宫格按键输入,有英文和数字两个模式,默认是数字模式,数字模式直接输出数字,英文模式连续按同一个按键会依次出现这个按键上的字母…...

基于AI大模型的医院SOP优化:架构、实践与展望
一、引言 1.1 研究背景与意义 近年来,人工智能(AI)技术取得了迅猛发展,尤其是大模型的出现,为各个领域带来了革命性的变化。在医疗领域,AI 医疗大模型正逐渐崭露头角,展现出巨大的应用潜力。随着医疗数据的海量积累以及计算能力的大幅提升,AI 医疗大模型能够对复杂的…...

Linux快速入门-一道简单shell编程题目
编写一个 Shell 程序。 功能:在用户家目录下创建一个文件夹myshell;进入此文件夹;在文件中创建文件aa.sh,如果文件夹或文件存在,则提示对象已存在,不创建。 代码编写 #!/bin/bash#获取用户家目录:方便后…...

Hive如何创建自定义函数(UDF)?
目录 1 自定义UDF函数基础 2 自定义UDF函数案例 3 创建临时函数 4 创建永久函数 1 自定义UDF函数基础 1. 内置函数:Hive 自带了一些函数...

聊聊前端框架中的process.env,env的来源及优先级(next.js、vue-cli、vite)
在平时开发中,常常使用vue、react相关脚手架创建项目,在项目根目录可以创建.env、.env.[mode](mode为development、production、test)、.env.local等文件,然后在项目中就可以通过process.env来访问相关的环境变量了。 下面针对如下…...

linux shell脚本 【分支结构case...in 、循环结构、函数】内附练习
1.思维导图 2.练习 1.定义一个find函数,查找ubuntu和root的gid 2.定义一个数组,写一个函数完成对数组的冒泡排序 bubble() {n${#arr[*]}for((i0;i<n-1;i));dofor((j0;j<n-1-i;j));doif ((arr[j]>arr[j1]));thentemp${arr[j]}arr[j]${arr[j1]}a…...

VSCode 终端显示“pnpm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本”
VSCode 终端显示“pnpm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本”VSCode 终端显示“pnpm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本”解决方案: 1.用get-ExecutionP…...

Android ActionBar 技术深度解析
Android ActionBar 技术深度解析 概述 ActionBar 是 Android 应用中的一个核心 UI 组件,用于提供导航、操作和品牌展示。它通常位于应用窗口的顶部,包含应用的标题、导航按钮、操作项等。ActionBar 自 Android 3.0(API 11)引入,并在 Android 5.0(API 21)后被 Toolbar …...

matlab-数字滤波器设计与实战
文章目录 数字滤波器设计FIR 滤波器设计IIR 滤波器设计巴特沃斯滤波器切比雪夫 I 型滤波器切比雪夫II型椭圆滤波器线性相位与非线性相位零相位响应数字滤波器实战数字滤波器产生延迟的主要原因补偿滤波引入的延迟补偿常量滤波器延迟补偿与频率有关的延迟从信号中除去不需要的频…...

JDK的运作原理
JDK(Java Development Kit)是Java开发者用来构建、编译、调试和运行Java应用程序的一套工具包。其核心原理涉及到Java语言的编译、执行以及Java虚拟机(JVM)的运作等多个方面。 1. Java编译原理 Java是一种先编译后解释执行的语言。…...