【驱动】SPI驱动分析(六)-RK SPI驱动分析
前言
Linux的spi接口驱动实现目录在kernel\drivers\spi
下。这个目录和一些层次比较明显的驱动目录布局不同,全放在这个文件夹下,因此还是只好通过看Kconfig 和 Makefile来找找思路
先看Makefile,里面关键几行:
obj-$(CONFIG_SPI_MASTER) += spi.o
//这个是针对有spi控制器的soc选项,一般的soc都有spi控制器吧。
# SPI master controller drivers (bus)
//下面的这些就是针对不同soc上的spi控制器的驱动了,我们可以通过make menuconfig的时候选上自己对应平台的
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
............
obj-$(CONFIG_SPI_AXI_SPI_ENGINE) += spi-axi-spi-engine.o
下面这些就是针对于主机作为spi从设备的时候用的,暂时貌似没支持,毕竟现实中几乎没有用过,而是作为master端出现
# SPI slave protocol handlers
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
再看Kconfig,第一个SPI选项我觉得有必要贴一下,首先只有选择它了才能进行后面的配置,其次它的help对spi的描述说的很清楚!
#
# SPI driver configuration
#
menuconfig SPIbool "SPI support"depends on HAS_IOMEMhelpThe "Serial Peripheral Interface" is a low level synchronousprotocol. Chips that support SPI can have data transfer ratesup to several tens of Mbit/sec. Chips are addressed with acontroller and a chipselect. Most SPI slaves don't supportdynamic device discovery; some are even write-only or read-only.SPI is widely used by microcontrollers to talk with sensors,eeprom and flash memory, codecs and various other controllerchips, analog to digital (and d-to-a) converters, and more.MMC and SD cards can be accessed using SPI protocol; and forDataFlash cards used in MMC sockets, SPI must always be used.SPI is one of a family of similar protocols using a four wireinterface (select, clock, data in, data out) including Microwire(half duplex), SSP, SSI, and PSP. This driver framework shouldwork with most such devices and controllers.
我们其次需要配上的选项就是SPI_MASTER
和CONFIG_SPI_ROCKCHIP
(我手上的是RK的SDK)。
config SPI_MASTER
# bool "SPI Master Support"booldefault SPIhelpIf your system has an master-capable SPI controller (whichprovides the clock and chipselect), you can enable thatcontroller and the protocol drivers for the SPI slave chipsthat are connected.config SPI_ROCKCHIPtristate "Rockchip SPI controller driver"helpThis selects a driver for Rockchip SPI controller.If you say yes to this option, support will be included forRK3066, RK3188 and RK3288 families of SPI controller.Rockchip SPI controller support DMA transport and PIO mode.The main usecase of this controller is to use spi flash as bootdevice.
于是从Makefile里得到如下语句和我们相关:
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
于是我们要分析的代码主要有:spi.c spi-rockchip.c
,瞬间没压力了,就两个文件,呵呵
下面主要从三个方面来分析spi框架
- spi控制器驱动的实现(毕竟spi控制器的驱动还是有可能要接触的)
- spi设备的驱动(我们更多的是编写设备的驱动,还是以eeprom为例吧,虽然我很想以spi接口的nor flash驱动为例,但是那又会牵涉出mtd子系统,这个留在mtd子系统分析吧)
- spi核心层的实现(上面1、2都是以各自的驱动实现为目标,并不深入到spi核心层,也就是至于spi核心层怎么为我们提供的服务不去关心,只需要按spi核心层使用它提供的服务就是了。所以现在统一分析spi核心层,看它是怎么提供的服务)
spi控制器驱动的实现
以spi-rockchip.c
为例,直接看module_platform_driver
:
static struct platform_driver rockchip_spi_driver = {.driver = {.name = DRIVER_NAME,.pm = &rockchip_spi_pm,.of_match_table = of_match_ptr(rockchip_spi_dt_match),},.probe = rockchip_spi_probe,.remove = rockchip_spi_remove,
};
平台驱动的内部流程就不分析了,直接看匹配成功后rockchip_spi_probe
的调用,但这里还是插入平台spi控制器设备端相关的代码:
- 使用
spi_alloc_master
函数为平台设备pdev
分配一个SPI主设备结构体,并将其大小设置为sizeof(struct rockchip_spi)
。这个函数会分配内存并初始化主设备结构体的各个字段。调用platform_set_drvdata
函数将主设备结构体指针保存在平台设备的私有数据中,以便后续在驱动的其他函数中可以访问该指针。使用spi_master_get_devdata
函数获取之前保存在私有数据中的主设备结构体指针rs
。
master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));if (!master)return -ENOMEM;platform_set_drvdata(pdev, master);rs = spi_master_get_devdata(master);
- 使用
platform_get_resource
函数获取SPI控制器的IO资源。这些资源包括寄存器地址、中断号等信息,向操作系统请求资源空间并建立起映射为以后所用。
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);rs->regs = devm_ioremap_resource(&pdev->dev, mem);if (IS_ERR(rs->regs)) {ret = PTR_ERR(rs->regs);goto err_ioremap_resource;}
- 使用
devm_clk_get
函数获取SPI控制器所需的时钟,包括"apb_pclk"和"spiclk"。这些时钟用于控制SPI控制器的时序和传输速率。
rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");if (IS_ERR(rs->apb_pclk)) {dev_err(&pdev->dev, "Failed to get apb_pclk\n");ret = PTR_ERR(rs->apb_pclk);goto err_ioremap_resource;}rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");if (IS_ERR(rs->spiclk)) {dev_err(&pdev->dev, "Failed to get spi_pclk\n");ret = PTR_ERR(rs->spiclk);goto err_ioremap_resource;}
- 使用
clk_prepare_enable
函数使获取到的时钟生效,确保SPI控制器可以正常工作。
ret = clk_prepare_enable(rs->apb_pclk);if (ret) {dev_err(&pdev->dev, "Failed to enable apb_pclk\n");goto err_ioremap_resource;}ret = clk_prepare_enable(rs->spiclk);if (ret) {dev_err(&pdev->dev, "Failed to enable spi_clk\n");goto err_spiclk_enable;}
- 调用
spi_enable_chip
函数使SPI芯片处于可用状态。这个函数会执行一些特定的SPI控制器寄存器的配置,以便使芯片可以正常通信。设置SPI主设备的属性。这些属性包括SPI总线类型、主设备指针、设备指针、最大频率等。
spi_enable_chip(rs, 0);rs->type = SSI_MOTO_SPI;rs->master = master;rs->dev = &pdev->dev;rs->max_freq = clk_get_rate(rs->spiclk);
- 使用
of_property_read_u32
函数从设备节点中读取属性值。在这个例子中,它读取了"rx-sample-delay-ns"属性,并将其存储在rsd_nsecs
变量中。调用get_fifo_len
函数获取FIFO的长度。FIFO用于在SPI传输过程中暂存数据。
if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",&rsd_nsecs))rs->rsd_nsecs = rsd_nsecs;rs->fifo_len = get_fifo_len(rs);
if (!rs->fifo_len) {dev_err(&pdev->dev, "Failed to get fifo length\n");ret = -EINVAL;goto err_get_fifo_len;
}
- 初始化自旋锁。自旋锁用于保护共享资源,防止多个进程同时访问造成冲突。设置设备的运行时PM状态为活动,并启用运行时PM。允许系统在不需要SPI设备时将其置于低功耗状态。
spin_lock_init(&rs->lock);pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- 设置主设备的一些属性,例如自动运行时PM、总线号、模式位、芯片选择数量、设备节点等。同时设置主设备的回调函数。这些回调函数将在SPI传输中的不同阶段被调用,以执行相应的操作
master->auto_runtime_pm = true;master->bus_num = pdev->id;master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;master->num_chipselect = 2;master->dev.of_node = pdev->dev.of_node;master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);master->set_cs = rockchip_spi_set_cs;master->prepare_message = rockchip_spi_prepare_message;master->unprepare_message = rockchip_spi_unprepare_message;master->transfer_one = rockchip_spi_transfer_one;master->handle_err = rockchip_spi_handle_err;
- 使用
dma_request_slave_channel
函数请求DMA通道,如果同时成功请求到了TX和RX的DMA通道,则设置DMA传输的地址和方向,并将相应的DMA通道设置为主设备的属性。
rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");if (IS_ERR_OR_NULL(rs->dma_tx.ch)) {/* Check tx to see if we need defer probing driver */if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) {ret = -EPROBE_DEFER;goto err_get_fifo_len;}dev_warn(rs->dev, "Failed to request TX DMA channel\n");}rs->dma_rx.ch = dma_request_slave_channel(rs->dev, "rx");if (!rs->dma_rx.ch) {if (rs->dma_tx.ch) {dma_release_channel(rs->dma_tx.ch);rs->dma_tx.ch = NULL;}dev_warn(rs->dev, "Failed to request RX DMA channel\n");}
- 使用
pinctrl_lookup_state
函数查找高速模式的引脚控制状态。检查查找高速模式的引脚控制状态是否成功。
rs->high_speed_state = pinctrl_lookup_state(rs->dev->pins->p,"high_speed");if (IS_ERR_OR_NULL(rs->high_speed_state)) {dev_warn(&pdev->dev, "no high_speed pinctrl state\n");rs->high_speed_state = NULL;}
- 使用
devm_spi_register_master
函数注册SPI主设备。
ret = devm_spi_register_master(&pdev->dev, master);if (ret) {dev_err(&pdev->dev, "Failed to register master\n");goto err_register_master;}
暂时不进入到spi核心层分析,这里我们只需要知道调用核心层的注册函数后,核心层会遍历所有注册到核心层的设备(实际最开始是加入到一个全局链表里,和I2C核心层的实现类似),然后尝试着添加每一个总线号为该控制器衍生出的总线号的设备到该spi控制器上,当然如果该spi控制器不支持某一个设备,那就取消添加这个设备,如果添加成功,那么该设备将会添加到spi总线上去,这条总线是spi核心层注册的,用来管理spi接口的设备和spi接口的驱动。
总结下probe函数的主要工作:
- 分配和初始化SPI主设备结构体。
- 获取并映射IO资源。
- 获取和使能时钟。
- 设置SPI主设备的属性和回调函数。
- 请求并设置DMA通道。
- 注册SPI主设备。
spi设备的驱动
以eeprom为例,我们分析下文件at25.c:
同样的,driver的注册过程我们就不深入了解了,其实就是一个总线设备驱动模型。我们直接看probe函数做了什么。
static const struct of_device_id at25_of_match[] = {{ .compatible = "atmel,at25", },{ }
};
MODULE_DEVICE_TABLE(of, at25_of_match);static struct spi_driver at25_driver = {.driver = {.name = "at25",.of_match_table = at25_of_match,},.probe = at25_probe,.remove = at25_remove,
};
- 检查
spi->dev.platform_data
是否存在,如果不存在,则调用at25_fw_to_chip
函数将固件信息转换为芯片描述,并将其存储在chip
结构体中。如果存在,则直接将spi->dev.platform_data
强制类型转换为spi_eeprom
结构体,并将其赋值给chip
。
/* Chip description */if (!spi->dev.platform_data) {err = at25_fw_to_chip(&spi->dev, &chip);if (err)return err;} elsechip = *(struct spi_eeprom *)spi->dev.platform_data;
- 根据
chip
结构体中的标志位判断EEPROM的地址长度是8位、16位还是24位,并将相应的值赋给addrlen
变量。
/* For now we only support 8/16/24 bit addressing */if (chip.flags & EE_ADDR1)addrlen = 1;else if (chip.flags & EE_ADDR2)addrlen = 2;else if (chip.flags & EE_ADDR3)addrlen = 3;else {dev_dbg(&spi->dev, "unsupported address type\n");return -EINVAL;}
- 通过发送
AT25_RDSR
指令读取EEPROM的状态寄存器。如果读取失败或状态寄存器中的AT25_SR_nRDY
位为1,表示EEPROM不可用,返回错误码-ENXIO
。
/* Ping the chip ... the status register is pretty portable,* unlike probing manufacturer IDs. We do expect that system* firmware didn't write it in the past few milliseconds!*/sr = spi_w8r8(spi, AT25_RDSR);if (sr < 0 || sr & AT25_SR_nRDY) {dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr);return -ENXIO;}
- 使用
devm_kzalloc
函数为at25_data
结构体分配内存,并使用GFP_KERNEL
标志指定内存分配的上下文。初始化互斥锁at25->lock
,用于保护共享资源的访问。将chip
结构体和spi
设备保存在at25_data
结构体中。使用spi_set_drvdata
函数将at25_data
结构体指针存储在spi
设备的私有数据中,以便在后续的函数中可以方便地访问。将地址长度addrlen
保存在at25_data
结构体的addrlen
字段中。
at25 = devm_kzalloc(&spi->dev, sizeof(struct at25_data), GFP_KERNEL);if (!at25)return -ENOMEM;mutex_init(&at25->lock);at25->chip = chip;at25->spi = spi_dev_get(spi);spi_set_drvdata(spi, at25);at25->addrlen = addrlen;
- 创建应用层用来操作的文件,使用
sysfs_bin_attr_init
函数初始化at25->bin
成员变量,其中at25->bin
是struct bin_attribute
类型的变量。设置at25->bin
的属性名称为"eeprom",访问权限为用户只读模式(S_IRUSR
)。设置at25->bin
的读回调函数为at25_bin_read
,写回调函数为at25_bin_write
。
sysfs_bin_attr_init(&at25->bin);at25->bin.attr.name = "eeprom";at25->bin.attr.mode = S_IRUSR;at25->bin.read = at25_bin_read;at25->mem.read = at25_mem_read;
- 根据
chip
的只读标志位(EE_READONLY
),确定是否将写回调函数和写权限添加到at25->bin
。
at25->bin.size = at25->chip.byte_len;if (!(chip.flags & EE_READONLY)) {at25->bin.write = at25_bin_write;at25->bin.attr.mode |= S_IWUSR;at25->mem.write = at25_mem_write;}
- 使用
sysfs_create_bin_file
函数将at25->bin
添加到SPI设备的内核对象(spi->dev.kobj
)中,以便将EEPROM字节通过sysfs导出。
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);if (err)return err;
- 如果
chip
的setup
字段不为空,将调用chip.setup
函数,并将at25->mem
和chip.context
作为参数传递。使用dev_info
函数打印一条设备信息消息,包括EEPROM的大小、名称、是否只读以及页面大小。
if (chip.setup)chip.setup(&at25->mem, chip.context);dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",(at25->bin.size < 1024)? at25->bin.size: (at25->bin.size / 1024),(at25->bin.size < 1024) ? "Byte" : "KByte",at25->chip.name,(chip.flags & EE_READONLY) ? " (readonly)" : "",at25->chip.page_size);
该代码的功能是在SPI设备上探测并初始化一个EEPROM芯片,然后将EEPROM的字节通过sysfs导出,以便其他内核代码或用户空间程序可以方便地访问和操作EEPROM数据。
spi核心层的实现
主要看spi.c文件:
static int __init spi_init(void);
postcore_initcall(spi_init);
从这里可以知道spi_init
的调用(也就是spi核心层的初始化)是在驱动加载前的。
调用kmalloc
函数为SPI子系统分配一个大小为SPI_BUFSIZ
的内核内存缓冲区,并将返回的指针赋值给buf
。
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {status = -ENOMEM;goto err0;
}
调用bus_register
函数注册SPI总线类型,将其添加到系统总线列表中。如果注册失败,将status
设置为返回的错误码,并跳转到err1
标签处进行错误处理。
status = bus_register(&spi_bus_type);
if (status < 0)goto err1;
调用class_register
函数注册SPI主控制器类,将其添加到系统设备类列表中。如果注册失败,将status
设置为返回的错误码,并跳转到err2
标签处进行错误处理。
status = class_register(&spi_master_class);
if (status < 0)goto err2;
所有注册到核心层的spi控制器都属于这个class。
相关文章:
【驱动】SPI驱动分析(六)-RK SPI驱动分析
前言 Linux的spi接口驱动实现目录在kernel\drivers\spi下。这个目录和一些层次比较明显的驱动目录布局不同,全放在这个文件夹下,因此还是只好通过看Kconfig 和 Makefile来找找思路 先看Makefile,里面关键几行: obj-$(CONFIG_SPI…...

【Linux】基础IO--文件基础知识/文件操作/文件描述符
文章目录 一、文件相关基础知识二、文件操作1.C语言文件操作2.操作系统文件操作2.1 比特位传递选项2.2 文件相关系统调用2.3 文件操作接口的使用 三、文件描述符fd1.什么是文件描述符2.文件描述符的分配规则 一、文件相关基础知识 我们对文件有如下的认识: 1.文件 …...

Intellij IDEA 的安装和使用以及配置
IDE有很多种,常见的Eclipse、MyEclipse、Intellij IDEA、JBuilder、NetBeans等。但是这些IDE中目前比较火的是Intellij IDEA(以下简称IDEA),被众多Java程序员视为最好用的Java集成开发环境,今天的主题就是IDEA为开发工…...

Zynq-Linux移植学习笔记之67- 国产ZYNQ上通过GPIO模拟MDC/MDIO协议
1、背景介绍 模块上有9个PHY,其中两个PHY通过ZYNQ PS端的MDIO总线连接,其余7个PHY单独通过GPIO进行控制,需要实现GPIO模拟MDC/MDIO协议。 2、vivado工程设计 vivado工程内为每个PHY建立两个GPIO IP核,分别用来代表MDC和MDIO&…...
Zookeeper(一)在WSL单机搭建Zookeeper伪集群
目录 Zookeeper1 启动单个Zookeeper实例1.1 下载Zookeeper安装包并解压1.2 添加环境变量1.3 修改默认配置1.4 新建数据存储目录和日志目录1.5 启动Zookeeper1.6 停止Zookeeper 2 搭建Zookeeper集群2.1 新建集群目录2.2 配置环境变量2.3 创建节点目录2.4 修改配置2.5 创建节点ID…...
QT(18):QString
目录 QStringQTypedArrayDataQTypedArrayDataQLatin1StringQStringLiteral乱码 QStringRef QString QString 存储16位QChar的字符串,其中每个QChar对应一个 UTF-16代码单元。QString 使用(写入时复制copy-on-write)来减少内存使用并避免不必…...

宏工科技通过CMMI三级认证,软件研发能力获国际权威认可
近日,宏工科技子公司湖南宏工软件成功通过CMMI三级认证并正式获得资质证书,斩获全球软件领域最权威的认证之一,标志着宏工科技在软件技术开发、研发管理、项目管理等多方面获得国际权威认证。 CMMI全称是Capability Maturity Model Integrati…...
2次MD5加密——用于分布式对话
用户端 : 指发起请求并与服务器进行交互的终端设备或应用程序。它可以是电脑、智能手机等。 用户端负责发送请求给服务端,并接收和处理服务端返回的响应。 服务端 : 是指提供服务、接收和处理用户端请求的计算机系统或应用程序。 它监听来自用…...

用Java制作简易版的王者荣耀
第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 创建类 GameFrame 运行类 package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import j…...

android 保活的一种有效的方法
android 保活的一种有效的方法 为什么要保活 说起程序的保活,其实很多人都觉得,要在手机上进行保活,确实是想做一些小动作,其实有些正常的场景也是需要我们进行保活的,这样可以增强我们的用户体验。保活就是使得程序…...

kibana安装
kibana安装下载注意事项 地址:curl -O https://artifacts.elastic.co/downloads/kibana/kibana-7.16.3-linux-x86_64.tar.gz 下载后直接解压启动即可 1. 但需要使用非root用户启动 ,root用户启动会报错 2. kibana需要和elasticsearch版本一致 不然…...

LV.12 D19 ADC实验 学习笔记
一、ADC简介 1.1 ADC ADC(Analog to Digital Converter)即模数转换器,指一个能将模拟信号转化为数字信号的电子元件 1.2 ADC主要参数 分辨率 ADC的分辨率一般以输出二进制数的位数来表示,当最大输入电压一定时,位数越高,…...

ubuntu配置免密登录vscode
1、配置免密登录 (1)在windows系统cmd下运行命令 ssh-keygen 一路回车,将会在C:\Users\用户名\.ssh目录下生成两个文件:id_rsa和id_rsa.pub。如下图所示。 (2)进入.ssh目录。如果想使用root用户࿰…...

软件工程--面向对象分析用通俗语言20小时爆肝总结!(包含用例图、活动图、类图、时序图......)
面向对象方法分为面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程(OOP),本文详细介绍面向对象分析 本文参考教材:沈备军老师的《软件工程原理》大多图片来源其中 目录 面向对…...
HarmonyOS—ArkTS中@Observed和@ObjectLink装饰器的嵌套类对象属性变化【鸿蒙专栏-11】
文章目录 ARKTS中@Observed和@ObjectLink装饰器的嵌套类对象属性变化@Observed 类装饰器说明装饰器参数类装饰器的使用@ObjectLink 变量装饰器说明装饰器参数同步类型允许装饰的变量类型被装饰变量的初始值举例装饰器的限制条件观察变化和行为表现观察的变化框架行为使用场景1.…...
网络通信安全的坚固防线双向认证技术详解
目录 什么是双向认证 双向认证的工作原理 双向认证的实现方式 双向认证的重要性 双向认证的挑战 安全最佳实践 小结 什么是双向认证 双向认证,又称为双向身份验证或双向鉴别,是一种在通信双方之间建立信任关系的安全机制。在通信过程中࿰…...

Appium+python+unittest搭建UI自动化框架
阅读本小节,需要读者具备如下前提条件: 1. 掌握一种编程语言基础,如java、python等。 2. 掌握一种单元测试框架,如java语言的testng框架、python的unittest框架。 3. 掌握目前主流的UI测试框架,移动端APP测试框架…...

使用paddledetection的记录
首先在这里使用的是是paddle--detection2.7的版本。 成功进行训练 目录: 目录 数据集准备 配置文件的修改 使用的是BML的平台工具: !python -m pip install paddlepaddle-gpu2.5 -i https://mirror.baidu.com/pypi/simple --user %cd /home/aistudio…...
MySQL数据库的备份与恢复
在管理MySQL数据库时,备份和恢复是保证数据安全和完整性的关键环节。本文将指导您如何有效地备份MySQL数据库,并在需要时进行数据恢复。 请注意,如果没有 mysql> 的标志,说明我们是在外面终端进行的操作 创建备份文件路径 在…...

Pycharm配置jupyter使用notebook详细指南(可换行conda环节)
本教程为事后记录,部分图片非实操图片。 详细记录了pycharm配置jupyter的方法,jupyter添加其他conda环境的方法,远程密码调用jupyter的方法,修改jupyter工作目录的方法。 文章目录 一、入门级配置1. Pycharm配置Conda自带的jupyt…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...