当前位置: 首页 > news >正文

Day5: platformDriver-1

Platform Driver (1)

Linux kernel中大部分设备可以归结为平台设备,因此大部分的驱动是平台驱动(patform driver)

什么是平台设备

平台设备是linux的设备模型中一类设备的抽象。 内核中的描述:

Platform devices are devices that typically appear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is direct addressing from a CPU bus. Rarely, a platform_device will be connected through a segment of some other kind of bus; but its registers will still be directly addressable.

一句话来描述就是: CPU能够直接寻址的SOC上的外设

请添加图片描述

图1:Platform设备

图1中的uart控制器,I2C控制器,GPIO控制器等,都是平台设备。

可以说,paltform设备对Linux驱动工程师是非常重要的,因为我们编写的大多数设备驱动,都是为了驱动plaftom设备。

Platform设备在内核中的实现主要包括三个部分:

  • Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;
  • Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;
  • Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform设备

对于图1中的I2C控制器挂载在platform Bus上,因此我们在linux kernel中常说的I2C driver,都是指I2C controller driver,都是以platform driver的形式存在,当然,对应的控制器是platform device。

与此同时,kernel抽象出I2C bus(/sys/bus/i2c),用于挂载和I2C controller通过I2C总线连接的各个I2C slave device。

串口驱动开发

驱动开发框架

得益于设备模型,Linux kernel平台驱动的开发有了一套非常固定的框架

1)模块的入口和出口
用于注册/注销platform driver,这一部分的代码基本固定,包括函数和变量的命名方式也可固定,如下:

 /* 驱动模块加载 */static int __init xxxdriver_init(void){return platform_driver_register(&xxx_driver);}/* 驱动模块卸载 */static void __exit xxxdriver_exit(void){platform_driver_unregister(&xxx_driver);}module_init(xxxdriver_init);module_exit(xxxdriver_exit);MODULE_LICENSE("GPL V2");MODULE_AUTHOR("QianRuShi-ABC");

2)platform driver

基本的platform driver包含三要素:struct platform_driver变量、probe/remove函数、用于和device tree匹配的match table,如下:

/** platform 平台驱动结构体*/
static struct platform_driver xxx_driver = {.driver = {.name = "xxx",.of_match_table = xxx_of_match,},.probe = xxx_probe,.remove = xxx_remove,
};/** platform 驱动的 probe 函数* 驱动与设备匹配成功以后此函数就会执行*/static int xxx_probe(struct platform_device *dev){match = of_match_device(xxx_of_match, &pdev->dev);if (!match) {dev_err(&pdev->dev, "Error: No device match found\n");return -ENODEV;}return 0;}static int xxx_remove(struct platform_device *dev){....../* 函数具体内容 */return 0;}/* 匹配列表 */static const struct of_device_id xxx_of_match[] = {{ .compatible = "xxx-xxx" },{ /* Sentinel */ }};

注意,xxx_of_match中的.compatible需要和DTS文件中的compatible对应,一般格式是“厂商名称,芯片系列-模块名”,例如“actions,s900-serial”

##串口驱动
串口设备(serial or uart,后面不再区分)是TTY设备的一种,Linux kernel为了方便串口驱动的开发,在TTY framework的基础上,封装了一层串口框架(serial framework)。该框架尽可能的屏蔽了TTY有关的技术细节(比较难懂),驱动工程师在编写串口驱动的时候,只需要把精力放在串口以及串口控制器本身即可。

Linux kernel serial framework位于“drivers/tty/serial”目录中,其软件架构(如下面图2所示)比较简单:
请添加图片描述

图2: serialFramework

Serial core是Serial framework的核心实现,对上封装、屏蔽TTY的技术细节,对下为具体的串口驱动提供简单、统一的编程API。
Serial drivers就是具体的串口驱动。

Serial Core

serial core主要实现如下三类功能
1)将串口设备有关的物理对象(及其操作方法)封装成一个一个的数据结构,以达到用软件语言描述硬件的目的。

2)向底层driver提供串口驱动的编程接口。

3)基于TTY framework所提供的TTY driver的编写规则,将底层driver看到的serial driver,转换为TTY driver,并将所有的serial操作,转换为对应的tty操作。

关键数据结构

struct uart_port
struct uart_state
struct uart_ops
struct uart_driver

API:

int uart_register_driver(struct uart_driver *uart);
void uart_unregister_driver(struct uart_driver *uart);int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_match_port(struct uart_port *port1, struct uart_port *port2);int uart_suspend_port(struct uart_driver *reg, struct uart_port *port);
int uart_resume_port(struct uart_driver *reg, struct uart_port *port);static inline int uart_tx_stopped(struct uart_port *port)extern void uart_insert_char(struct uart_port *port, unsigned int status,unsigned int overrun, unsigned int ch, unsigned int flag);

串口驱动的移植步骤

  • 定义并注册uart driver
  • 注册uart port
  • 定义并实现uart ops

定义并注册uart driver

static struct uart_driver imx_reg = {.owner          = THIS_MODULE,.driver_name    = DRIVER_NAME,.dev_name       = DEV_NAME,.major          = SERIAL_IMX_MAJOR,.minor          = MINOR_START,.nr             = ARRAY_SIZE(imx_ports),.cons           = IMX_CONSOLE,
};

注册uart port

platform device代表uart控制器,是实体抽象。对应的,uart port代表“串口”. 因此,我们需要在platform device probe的时候(platform driver的probe接口),动态分配并注册一个uart port(struct uart_port)。在后续的串口操作中,都是以uart port指针为对象. 见下一章节代码中i.mx6的驱动代码。

定义并实现uart ops

struct uart_ops结构包含了各式各样的uart端口的操作函数,需要在添加uart port的时候提供.见下一章节代码中i.mx6的驱动代码

完整代码

对应驱动开发框架中,1)模块的入口和出口
static int __init imx_serial_init(void)
{int ret = uart_register_driver(&imx_reg);if (ret)return ret;ret = platform_driver_register(&serial_imx_driver);if (ret != 0)uart_unregister_driver(&imx_reg);return ret;
}static void __exit imx_serial_exit(void)
{platform_driver_unregister(&serial_imx_driver);uart_unregister_driver(&imx_reg);
}module_init(imx_serial_init);
module_exit(imx_serial_exit);MODULE_AUTHOR("Sascha Hauer");
MODULE_DESCRIPTION("IMX generic serial port driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:imx-uart");
对应驱动开发框架中,2)platform driver

基本的platform driver包含三要素:struct platform_driver变量、probe/remove函数、用于和device tree匹配的match table

  • struct platform_driver变量
static struct platform_driver serial_imx_driver = {.probe		= serial_imx_probe,.remove		= serial_imx_remove,.suspend	= serial_imx_suspend,.resume		= serial_imx_resume,.id_table	= imx_uart_devtype,.driver		= {.name	= "imx-uart",.of_match_table = imx_uart_dt_ids,},
};
  • robe/remove函数
serial_imx_probe
 
static int serial_imx_probe(struct platform_device *pdev)
{struct imx_port *sport;void __iomem *base;int ret = 0;struct resource *res;int txirq, rxirq, rtsirq;sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);if (!sport)return -ENOMEM;ret = serial_imx_probe_dt(sport, pdev);if (ret > 0)serial_imx_probe_pdata(sport, pdev);else if (ret < 0)return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))return PTR_ERR(base);rxirq = platform_get_irq(pdev, 0);
txirq = platform_get_irq(pdev, 1);
rtsirq = platform_get_irq(pdev, 2);sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
sport->port.membase = base;
sport->port.type = PORT_IMX,
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.ops = &imx_pops;
sport->port.rs485_config = imx_rs485_config;
sport->port.rs485.flags =SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
sport->port.flags = UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function = imx_timeout;
sport->timer.data     = (unsigned long)sport;sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {ret = PTR_ERR(sport->clk_ipg);dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);return ret;
}sport->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(sport->clk_per)) {ret = PTR_ERR(sport->clk_per);dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);return ret;
}sport->port.uartclk = clk_get_rate(sport->clk_per);
if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);if (ret < 0) {dev_err(&pdev->dev, "clk_set_rate() failed\n");return ret;}
}
sport->port.uartclk = clk_get_rate(sport->clk_per);/** Allocate the IRQ(s) i.MX1 has three interrupts whereas later* chips only have one interrupt.*/
if (txirq > 0) {ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,dev_name(&pdev->dev), sport);if (ret)return ret;ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,dev_name(&pdev->dev), sport);if (ret)return ret;
} else {ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,dev_name(&pdev->dev), sport);if (ret)return ret;
}
imx_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
return uart_add_one_port(&imx_reg, &sport->port);

}

serial_imx_remove

static int serial_imx_remove(struct platform_device *pdev)
{struct imx_port *sport = platform_get_drvdata(pdev);return uart_remove_one_port(&imx_reg, &sport->port);
}

  • 用于和device tree匹配的match table
static const struct of_device_id imx_uart_dt_ids[] = {{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
对应串口驱动的移植步骤中,定义并注册uart driver

在驱动init函数中注册了uart driver

int ret = uart_register_driver(&imx_reg);

在linux serial framework中,uart driver是一个平行于platform driver的概念,用于驱动“虚拟”的“串口”设备。

#define DRIVER_NAME "IMX-uart"static struct uart_driver imx_reg = {.owner          = THIS_MODULE,.driver_name    = DRIVER_NAME,.dev_name       = DEV_NAME,.major          = SERIAL_IMX_MAJOR,.minor          = MINOR_START,.nr             = ARRAY_SIZE(imx_ports),.cons           = IMX_CONSOLE,
};
对应串口驱动的移植步骤中,注册uart port

假如一个soc中有5个串口控制器(也可称作uart控制器,后面我们不再区分),每个uart控制器都可引出一个串口(uart port)。那么:
每个uart控制器,都是一个platform device,由dts文件的一个node描述。而这5个platform device,可由同一个driver驱动,即platform driver。
相对于uart控制器实实在在的存在,我们更为熟悉的串口(uart port),则是虚拟的设备,它们由“struct uart_port”描述(后面会介绍),并在platform driver的probe接口中,注册到kernel。它们可由同一个driver驱动,即这里所说的uart driver。

struct imx_port {struct uart_port	port;struct timer_list	timer;unsigned int		old_status;unsigned int		have_rtscts:1;unsigned int		dte_mode:1;unsigned int		irda_inv_rx:1;unsigned int		irda_inv_tx:1;unsigned short		trcv_delay; /* transceiver delay */struct clk		*clk_ipg;struct clk		*clk_per;const struct imx_uart_data *devdata;/* DMA fields */unsigned int		dma_is_inited:1;unsigned int		dma_is_enabled:1;unsigned int		dma_is_rxing:1;unsigned int		dma_is_txing:1;struct dma_chan		*dma_chan_rx, *dma_chan_tx;struct scatterlist	tx_sgl[2];struct imx_dma_rxbuf	rx_buf;unsigned int		tx_bytes;unsigned int		dma_tx_nents;struct delayed_work	tsk_dma_tx;wait_queue_head_t	dma_wait;unsigned int            saved_reg[10];
#define DMA_TX_IS_WORKING 1unsigned long		flags;
};

在platform driver的probe函数中会动态分配并注册一个uart port(struct uart_port)然后初始化并注册其中的port变量。初始化完之后,直接调用uart_add_one_port接口,将该port添加到kernel serial core

再返回上面的static int serial_imx_probe(struct platform_device *pdev)展开看到

//分配struct xxx_port类型的指针
struct imx_port *sport;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
//获取中断号,该串口对应的中断号, 一般是从DTS中解析得到的;rxirq = platform_get_irq(pdev, 0);txirq = platform_get_irq(pdev, 1);rtsirq = platform_get_irq(pdev, 2);
//初始化并注册其中的port变量sport->port.dev = &pdev->dev;sport->port.mapbase = res->start;sport->port.membase = base;sport->port.type = PORT_IMX,sport->port.iotype = UPIO_MEM;sport->port.irq = rxirq;sport->port.fifosize = 32;sport->port.ops = &imx_pops;sport->port.rs485_config = imx_rs485_config;sport->port.rs485.flags =SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;sport->port.flags = UPF_BOOT_AUTOCONF;init_timer(&sport->timer);sport->timer.function = imx_timeout;sport->timer.data     = (unsigned long)sport;

在proble函数的最后,调用uart_add_one_port接口,将该port添加到kernel serial core
return uart_add_one_port(&imx_reg, &sport->port);

对应串口驱动的移植步骤中,定义并实现uart ops

上面platform driver的probe函数serial_imx_probe,展开中看到
sport->port.ops = &imx_pops;
struct uart_ops结构包含了各式各样的uart端口的操作函数,需要在添加uart port的时候提供。

static struct uart_ops imx_pops = {.tx_empty	= imx_tx_empty,.set_mctrl	= imx_set_mctrl,.get_mctrl	= imx_get_mctrl,.stop_tx	= imx_stop_tx,.start_tx	= imx_start_tx,.stop_rx	= imx_stop_rx,.enable_ms	= imx_enable_ms,.break_ctl	= imx_break_ctl,.startup	= imx_startup,.shutdown	= imx_shutdown,.flush_buffer	= imx_flush_buffer,.set_termios	= imx_set_termios,.type		= imx_type,.config_port	= imx_config_port,.verify_port	= imx_verify_port,
#if defined(CONFIG_CONSOLE_POLL).poll_init      = imx_poll_init,.poll_get_char  = imx_poll_get_char,.poll_put_char  = imx_poll_put_char,
#endif
};

个人公众号交流
请添加图片描述

#参考
http://www.wowotech.net/x_project/serial_driver_porting_1.html
http://www.wowotech.net/x_project/serial_driver_porting_2.html
http://www.wowotech.net/x_project/serial_driver_porting_3.html
http://www.wowotech.net/x_project/serial_driver_porting_4.html

http://www.wowotech.net/comm/serial_overview.html

相关文章:

Day5: platformDriver-1

Platform Driver (1) Linux kernel中大部分设备可以归结为平台设备&#xff0c;因此大部分的驱动是平台驱动&#xff08;patform driver&#xff09; 什么是平台设备 平台设备是linux的设备模型中一类设备的抽象。 内核中的描述&#xff1a; Platform devices are devices t…...

开发手册——一、编程规约_7.控制语句

这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】在一个 switch 块内&#xff0c;每个 case 要么通过 break / return 等来终止&#xff0c;要么注释说明程序将继续执行到哪…...

python每日学9 : windows上配置gitee的远程仓库,git的初步使用

在开发中&#xff0c;如果遇到复杂的项目&#xff0c;使用版本控制是非常有必要的&#xff0c;如果涉及到多端开发&#xff0c;那么还需要使用远程仓库。本文作个简单记录&#xff0c;记录下git初步使用。 1 下载与安装 git还有几个ui版本&#xff0c;但是开始使用的话&#…...

精确率与召回率,ROC曲线与PR曲线

精确率与召回率&#xff0c;ROC曲线与PR曲线 在机器学习的算法评估中&#xff0c;尤其是分类算法评估中&#xff0c;我们经常听到精确率(precision)与召回率(recall)&#xff0c;ROC曲线与PR曲线这些概念&#xff0c;那这些概念到底有什么用处呢&#xff1f; 首先&#xff0c…...

现代操作系统——Linux架构与学习

小白的疑惑 在我决定从事嵌入式&#xff08;应用层&#xff09;方面的工作时&#xff0c;我查询了大量资料该如何学习&#xff0c;几乎所有观点不约而同的都指向了学习好Linux&#xff0c;大部分工作都是在Linux环境下来进行工作的。于是我雄心勃勃的去下载Linux&#xff0c;可…...

中文代码82

PK 嘚釦 docProps/PK 嘚釦羸 r docProps/app.xml潙蚽?勶曻Q顗濔S? 錞礖剅D柍珘m?鳞?ぷ辷f硌?2?upc厭Y樐8 rU y搪m眾&a?珪?紓 玺鶋瑣襚? ?i嘲rN?布倖儇?攊橌??嚗猝)芻矂2吟腊K湞?CK臶>鸘\?ΔF滋齢q旮T?桀?;偉 A軥v蕯朾偤佷3?е…...

顺序表(一篇带你掌握顺序表)

目录 一、顺序表是什么 1.1 概念 1.2 分类 1.3 结构 二、顺序表的基本操作 2.1 前绪准备 2.2 初始化 2.3 扩容 2.5 尾插 2.6 打印 2.7 尾删 2.8 头插 2.9 头删 2.10 在pos位置插入 2.11 删除pos位置的数据 2.12 查找 三、完整代码 3.1 Test.c文件 3.2 SeqList.h…...

【SpringCloud】SpringCloud教程之Feign实战

目录前言SpringCloud Feign远程服务调用一.需求二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用(order服务内编写)四.构建Feign(order服务内配置)五.自定义Feign配置(order服务内配置)六.Feign配置日志(oder服务内配置)七.Feign调优(order服务内配置)八.抽离Feign前…...

嵌入式linux必备内存泄露检测神器

Valgrind介绍 Valgrind是一个可移植的动态二进制分析工具集&#xff0c;主要用于发现程序中的内存泄漏、不合法内存访问、使用未初始化的内存、不正确的内存释放以及性能问题等&#xff0c;可在Linux和Mac OS X等平台上使用。 Valgrind由多个工具组成&#xff0c;其中最常用的…...

设计模式之行为型模式

四、行为型模式 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式&#xff0c;前者采用继承机制来在…...

解密 三岁的三岁到底为什么叫做三岁?

机缘 那一年&#xff0c;一次奇奇怪怪的挫折与一次奇奇怪怪的成长。 在学习Python的路上总觉得少了点什么&#xff0c;是心情&#xff1f;是机遇&#xff1f;还是力量&#xff1f; 都不是又都是&#xff01; 缺少一个实践和记忆的平台 记性不好是硬伤 前一天学的下一秒就忘记了…...

id选择器

id选择器可以为特定的id的标签进行css美化 使用方法&#xff1a; 标签内设好 id值&#xff0c; CSS的id选择器以“#id名”来调用 注意 所有标签都有id值id属性值类似于身份证号码&#xff0c;在一个页面中是唯一的值&#xff0c;不可重复一个标签上只能有一个id属性值一个id属性…...

《科技之巅3》读书笔记

文章目录书籍信息人工智能&#xff0c;“吃一堑长一智”的机器人机交互&#xff0c;为解决“交流障碍”问题而生硬件与算法&#xff0c;好马还需好鞍模式创新&#xff0c;赋予技术新的定义云与数据共享&#xff0c;灵活应对信息的爆发式增长“机器人”&#xff0c;从电影和小说…...

18.用于大型程序的工具

文章目录用于大型程序的工具18.1异常处理18.1.1抛出异常栈展开栈展开过程中对象被自动销毁析构函数与异常异常对象18.1.2捕获异常查找匹配的处理代码重新抛出捕获所有异常的处理代码18.1.3函数try语句块与构造函数18.1.4noexcept异常说明违反异常说明异常说明的实参noexcept运算…...

mysql一主键uuid和自增的选择

文章目录 1.自增ID的优缺点1.1 优点1.2 缺点1.3 不适合以自增ID主键作为主键的情况2.UUID作为主键2.1 介绍2.2 优点2.3 缺点3.有序UUID作为主键3.1 介绍3.2 演示使用3.2.1 前提知识3.2.1.1 数据类型 - binary3.2.1.2 函数 - hex()3.2.1.3 函数 - unhex()3.2.2 数据库层3.2.3 JA…...

【EDA工具使用】——VCS和Verdi的联合仿真的简单使用

目录 1.芯片开发所需的工具环境 2.编译仿真工具 3.三步式混合编译仿真&#xff08;最常用&#xff09;​编辑 4.两步式混合编译仿真​编辑 5.VCS的使用 ​6.verdi的使用 1.产生fsdb文件的两种方法​编辑 1.芯片开发所需的工具环境 2.编译仿真工具 3.三步式混合编译仿真…...

【Java学习笔记】4.Java 对象和类

前言 本章介绍Java的对象和类。 Java 对象和类 Java作为一种面向对象语言。支持以下基本概念&#xff1a; 多态继承封装抽象类对象实例方法重载 本节我们重点研究对象和类的概念。 对象&#xff1a;对象是类的一个实例&#xff08;对象不是找个女朋友&#xff09;&#x…...

39. 实战:基于api接口实现视频解析播放(32接口,窗口化操作,可导出exe,附源码)

目录 前言 目的 思路 代码实现 需要导入的模块 1. 导入解析网站列表&#xff0c;实现解析过程 2. 设计UI界面 3. 设置窗口居中和循环执行 4. 注意事项 完整源码 运行效果 总结 前言 本节将类似34. 实战&#xff1a;基于某api实现歌曲检索与下载&#xff08;附完整…...

基于灵动 MM32 微控制器的便携式血氧仪方案

基于灵动 MM32 微控制器的便携式血氧仪&#xff1a; - Cortex-M0() 最高主频 72MHz 可实现血氧饱和度信号采集、算法操作和 LED 显示操作 - 高性能的 1Msps 12b ADC 能对光电采样结果进行大数据量的暂存和处理&#xff0c;提高采样的效率并有助于对结果做高精度的计算 - 100…...

2022秋-2023-中科大-数字图像分析-期末考试试卷回忆版

今天晚上刚考完&#xff0c;心累&#xff0c;在这里继续授人以渔(仅供参考&#xff0c;切勿对着复习不看ppt&#xff0c;ppt一定要过两遍)。 注意:往年的经验贴&#xff0c;到此为止&#xff0c;全部作废&#xff0c;一个没考。千万不要只对着复习&#xff0c;SIFT没考&#x…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...

今日行情明日机会——20250609

上证指数放量上涨&#xff0c;接近3400点&#xff0c;个股涨多跌少。 深证放量上涨&#xff0c;但有个小上影线&#xff0c;相对上证走势更弱。 2025年6月9日涨停股主要行业方向分析&#xff08;基于最新图片数据&#xff09; 1. 医药&#xff08;11家涨停&#xff09; 代表标…...

关于 ffmpeg设置摄像头报错“Could not set video options” 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/148515355 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…...

Linux系统:进程间通信-匿名与命名管道

本节重点 匿名管道的概念与原理匿名管道的创建命名管道的概念与原理命名管道的创建两者的差异与联系命名管道实现EchoServer 一、管道 管道&#xff08;Pipe&#xff09;是一种进程间通信&#xff08;IPC, Inter-Process Communication&#xff09;机制&#xff0c;用于在不…...