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

使用Linux驱动程序的fasync(文件异步通知机制)向用户空间发送SIGIO信号的学习记录

前言

本文学习使用Linux驱动程序的fasync(文件异步通知机制)向用户空间发送SIGIO信号。

fasync(文件异步通知机制)名字的来历

fasync“file asynchronous” 的缩写,意思是 文件异步通知
这里的文件是指文件结构体struct file *file ,关于文件结构体,我在前面的博文中已经详细介绍过了,说情请看 https://blog.csdn.net/wenhao_ir/article/details/144927750。

重点是文件描述符(fd)记录的相关信息就存储于文件结构体struct file *file 中的。

由于整个异步通知机制是借助于设备文件的文件描述符(fd)进行的操作,所以这种异步通知称为叫做“文件异步通知”,在Linux中缩写为fasync

本文代码源自之前的哪篇博文

本文的代码是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的基础上修改而来的,把其中的wait_event_interruptible异步通知机制去掉,然后加入本文要学习的fasync(文件异步通知机制)。

完整源代码

驱动程序代码(gpio_key_drv.c)

#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>struct gpio_key{int gpio;struct gpio_desc *gpiod;int flag;int irq;
} ;static struct gpio_key *gpio_keys_100ask;/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_key_class;static int g_key = 0;struct fasync_struct *button_fasync;/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;#define NEXT_POS(x) ((x+1) % BUF_LEN)static int is_key_buf_empty(void)
{return (r == w);
}static int is_key_buf_full(void)
{return (r == NEXT_POS(w));
}static void put_key(int key_value)
{if (!is_key_buf_full()){g_keys[w] = key_value;w = NEXT_POS(w);}
}static int get_key(void)
{int key_value = 0;if (!is_key_buf_empty()){key_value = g_keys[r];r = NEXT_POS(r);}return key_value;
}/* 实现文件操作结构体中的read函数  */
static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);int err;int key_value;//从缓形缓冲区中取出数据key_value = get_key();err = copy_to_user(buf, &key_value, 4);// 返回值为4表明读到了4字节的数据return 4;
}static int gpio_key_drv_fasync(int fd, struct file *file, int on)
{if (fasync_helper(fd, file, on, &button_fasync) >= 0)return 0;elsereturn -EIO;
}/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {.owner	 = THIS_MODULE,.read    = gpio_key_drv_read,.fasync  = gpio_key_drv_fasync,
};static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_key *gpio_key = dev_id;int val;// 返回引脚电平的逻辑值,注意:如果是低电平有效,则当物理电平为低电平时,其返回值为1;则当物理电平为高电平时,其返回值为0.// 如果要得到物理电平值,可以用函数gpiod_get_raw_value()得到val = gpiod_get_value(gpio_key->gpiod);// 打印中断号、GPIO引脚编号和电平值// printk("Interrupt number: %d; GPIO pin number: %d; Pin Logical value: %d\n", irq, gpio_key->gpio, val);// g_key的高8位中存储的是GPIO口的编号,低8位中存储的是按键按下时的逻辑值g_key = (gpio_key->gpio << 8) | val;//装按键值放入环形缓冲区put_key(g_key);kill_fasync(&button_fasync, SIGIO, POLL_IN);return IRQ_HANDLED;  // 表示中断已处理
}/* 1. 从platform_device获得GPIO* 2. gpio=>irq* 3. request_irq*/
static int gpio_key_probe(struct platform_device *pdev)
{int err;// 获取设备树节点指针struct device_node *node = pdev->dev.of_node;// count用于存储设备树中描述的GPIO口的数量int count;int i;enum of_gpio_flags flag;unsigned flags = GPIOF_IN;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);count = of_gpio_count(node);if (!count){printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);return -1;}gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);if (!gpio_keys_100ask) {printk("Memory allocation failed for gpio_keys_100ask\n");return -ENOMEM;}for (i = 0; i < count; i++){//  获取GIPO的全局编号及其标志位信息的代码gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag);if (gpio_keys_100ask[i].gpio < 0){printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);return -1;}// 获取GPIO口的GPIO描述符的代码gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);if (!gpio_keys_100ask[i].gpiod) {printk("Failed to get GPIO descriptor for GPIO %d\n", gpio_keys_100ask[i].gpio);return -EINVAL;}// 结构体gpio_key的成员flag用于存储对应的GPIO口是否是低电平有效,假如是低电平有效,成员flag的值为1,假如不是低电平有效,成员flag的值为0。// 后续代码实际上并没有用到成员flag,这里出现这句代码只是考虑到代码的可扩展性,所以在这里是可以删除的。gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW;// 每次循环都重新初始化flagsflags = GPIOF_IN;// 假如GPIO口是低电平有效,则把flags添加上低电平有效的信息if (flag & OF_GPIO_ACTIVE_LOW)flags |= GPIOF_ACTIVE_LOW;// 请求一个GPIO硬件资源与设备结构体`pdev->dev`进行绑定// 注意,这个绑定操作会在调用函数platform_driver_unregister()注销platform_driver时自动由内核解除绑定操作,所以gpio_key_remove函数中不需要显示去解除绑定// 由`devm`开头的函数通常都会内核自动管理资源,咱们在退出函数中不用人为的去释放资源或解除绑定。err = devm_gpio_request_one(&pdev->dev, gpio_keys_100ask[i].gpio, flags, NULL);// 获取GPIO口的中断请求号gpio_keys_100ask[i].irq  = gpio_to_irq(gpio_keys_100ask[i].gpio);}for (i = 0; i < count; i++){char irq_name[32];  // 用于存储动态生成的中断名称//使用snprintf()函数将动态生成的中断名称写入irq_name数组snprintf(irq_name, sizeof(irq_name), "swh_gpio_irq_%d", i);  // 根据i生成名称//调用函数request_irq()来请求并设置一个中断err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_FALLING, irq_name, &gpio_keys_100ask[i]);}/* 注册file_operations 	*/major = register_chrdev(0, "swh_read_keys_major", &gpio_key_drv);  gpio_key_class = class_create(THIS_MODULE, "swh_read_keys_class");if (IS_ERR(gpio_key_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "swh_read_keys_major");return PTR_ERR(gpio_key_class);}// 由于这里是把多个按键看成是一个设备,你可以想像一个键盘上对应多个按键,但键盘本身是一个设备,所以只有一个设备文件device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, "read_keys0"); /* /dev/read_keys0 */return 0;}static int gpio_key_remove(struct platform_device *pdev)
{struct device_node *node = pdev->dev.of_node;int count;int i;device_destroy(gpio_key_class, MKDEV(major, 0));class_destroy(gpio_key_class);unregister_chrdev(major, "swh_read_keys_major");count = of_gpio_count(node);for (i = 0; i < count; i++) {// 只有在irq有效时才释放中断资源if (gpio_keys_100ask[i].irq >= 0) {// 释放GPIO中断资源,下面这句代码做了下面两件事:// 1、解除 `gpio_keys_100ask[i].irq` 中断号和 `gpio_key_isr` 中断处理函数的绑定。// 2、解除 `gpio_keys_100ask[i].irq` 中断号和中断处理函数与 `gpio_keys_100ask[i]` 数据结构的绑定。free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);}// 释放GPIO描述符if (gpio_keys_100ask[i].gpiod) {gpiod_put(gpio_keys_100ask[i].gpiod);}}// 释放内存kfree(gpio_keys_100ask);return 0;
}static const struct of_device_id irq_matach_table[] = {{ .compatible = "swh-gpio_irq_key" },{ },
};/* 1. 定义platform_driver */
static struct platform_driver gpio_keys_driver = {.probe      = gpio_key_probe,.remove     = gpio_key_remove,.driver     = {.name   = "swh_irq_platform_dirver",.of_match_table = irq_matach_table,},
};/* 2. 在入口函数注册platform_driver */
static int __init gpio_key_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = platform_driver_register(&gpio_keys_driver); return err;
}/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*     卸载platform_driver*/
static void __exit gpio_key_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(&gpio_keys_driver);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(gpio_key_init);
module_exit(gpio_key_exit);MODULE_LICENSE("GPL");

测试程序代码


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>static int fd;
static void sig_func(int sig)
{int val;read(fd, &val, 4);/* 提取 GPIO 编号和逻辑值 */int gpio_number = (val >> 8) & 0xFF; // 高8位为 GPIO 编号int gpio_value = val & 0xFF;         // 低8位为逻辑值/* 打印读到的信息 */printf("GPIO Number: %d, Logical Value: %d\n", gpio_number, gpio_value);}int main(int argc, char **argv)
{int val;struct pollfd fds[1];int timeout_ms = 5000;int ret;int	flags;/* 1. 判断参数 */if (argc != 2) {printf("Usage: %s <dev>\n", argv[0]);return -1;}signal(SIGIO, sig_func);/* 2. 打开文件 */fd = open(argv[1], O_RDWR);if (fd == -1){printf("can not open file %s\n", argv[1]);return -1;}fcntl(fd, F_SETOWN, getpid());flags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flags | FASYNC);while (1){printf("https://blog.csdn.net/wenhao_ir \n");sleep(5);}close(fd);return 0;
}

使用fasync处理按键中断的缺陷说明

由于是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的基础上修改而来的,而博文 https://blog.csdn.net/wenhao_ir/article/details/145228617的功能是处理开发板上按键产生的中断,这就存在一个问题,当按键按得过快时,短时间内连续发送多次信号以致于用户空间的信号处理函数上一次还没执行完,下一次的信号又来时Linux内核会怎么处理?
答:在 Linux 内核中,当中断处理程序中使用 kill_fasync 向用户空间发送信号时,如果信号的接收方(用户空间进程)的信号处理函数还没有执行完,新的信号到来时,Linux 会将信号合并为一次信号,这意味着相同类型的信号不会排队执行。

假设用户空间注册了 SIGIO 的信号处理函数 sig_func,按键被快速按下,触发多个 kill_fasync(&button_fasync, SIGIO, POLL_IN) 调用:

  • 第一步:按键第一次按下,kill_fasync 发送 SIGIO,用户空间的 sig_func 开始运行。
  • 第二步:按键第二次按下,kill_fasync 再次发送 SIGIO,但内核不会排队多次 SIGIO 信号,而是仅标记目标进程仍然需要处理该信号。
  • 第三步:用户空间处理完 sig_func 后,如果按键已经触发了多次信号,用户空间进程感知到的只是有一次信号到达,这就会导致按键信息丢失。

所以,其实本文代码中实现的fasync(文件异步通知机制),在实际工作应用中,并不会用来处理按键中断,本文只是借助于它来学习这种异步通知机制,真的要处理按键中断的话还是博文https://blog.csdn.net/wenhao_ir/article/details/145228617 中运用的wait_event_interruptible异步通知机制不错。

相关函数fcntl()的介绍

fcntl 是 Linux 系统中的一个强大的文件控制函数,用于操作文件描述符(fd)。通过 fcntl,可以设置文件描述符(fd)的属性、锁定文件或获取文件状态。


函数原型

#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );

参数说明

  1. fd:需要操作的文件描述符。
  2. cmd:控制命令,决定 fcntl 作什么操作。
  3. arg:可选参数,根据 cmd 的不同而不同,有时可以省略。

参数cmd的常用命令及功能

1. 设置文件描述符的属主(属于哪个进程)

  • 命令F_SETOWN
  • 作用:设置当前文件描述符的“属主”(owner),在本文的代码中,即设置到哪底由哪个进程或线程接收来自与设备文件相关的驱动程序发来的信号。
  • 信号类型
    • SIGIO:异步 I/O 事件(如套接字有可读/可写事件)。
    • SIGURG:紧急数据到达(如 TCP 的 out-of-band 数据)。

2. 获取/设置文件描述符标志

  • F_GETFD:获取文件描述符标志(如 FD_CLOEXEC)。
  • F_SETFD:设置文件描述符标志。

3. 获取/设置文件状态标志

  • F_GETFL:获取文件状态标志(如 O_RDONLY, O_NONBLOCK)。
  • F_SETFL:设置文件状态标志。
    • 常用标志:
      • O_APPEND:追加模式。
      • O_NONBLOCK:非阻塞模式。
      • O_SYNC:同步写入模式。

4. 文件锁操作

  • F_GETLK:获取文件锁的状态。

  • F_SETLK:设置文件锁(非阻塞)。

  • F_SETLKW:设置文件锁(阻塞)。

    锁类型:

    • F_RDLCK:共享读锁。
    • F_WRLCK:独占写锁。
    • F_UNLCK:解锁。

5. 文件描述符复制

  • F_DUPFD:复制文件描述符,返回新的文件描述符(从指定起点 arg 开始选择最小的可用描述符)。
  • F_DUPFD_CLOEXEC:类似于 F_DUPFD,但自动设置 FD_CLOEXEC 标志。

返回值

  • 成功:返回与命令相关的值。
  • 失败:返回 -1,并设置 errno

代码详解:与fasync(文件异步通知机制)有关的代码

首先在用户空间的测试程序中用函数signal()为当前进程(注意是进程,信号的接收对象是进程)设置一个信号类型为SIGIO的信号处理函数sig_func(),这样,当进程收到类型为SIGIO的信号,就会去调用函数sig_func(),代码如下:

static void sig_func(int sig)
{int val;read(fd, &val, 4);/* 提取 GPIO 编号和逻辑值 */int gpio_number = (val >> 8) & 0xFF; // 高8位为 GPIO 编号int gpio_value = val & 0xFF;         // 低8位为逻辑值/* 打印读到的信息 */printf("GPIO Number: %d, Logical Value: %d\n", gpio_number, gpio_value);}signal(SIGIO, sig_func);

然后测试程序打开驱动程序创建的设备文件,在这里设备文件的路径为:

/dev/read_keys0

相关的代码如下:

	fd = open(argv[1], O_RDWR);if (fd == -1){printf("can not open file %s\n", argv[1]);return -1;}

获得设备文件描述符fd后,就通过函数fcntl()把进程号写入到设备文件描述符fd对应的文件结构体struct file *file中,当然,你也可以认为是把进程号写入到了文件描述符fd中。
关于文件结构体struct file *file的详细介绍,见我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/144927750
文件描述符fd对应的文件结构体struct file *file最终会以输入参数的形式传递给驱动程序中的文件操作结构结构体中的操作函数。
相关代码如下:

fcntl(fd, F_SETOWN, getpid());

这里请先了解下函数fcntl(),我在上面已经介绍过了,这里就不再介绍了。
这句代码的具体作用是设置当前进程号为接收来自设备文件描述符fd关联的驱动程序发来的信号的进程。

接下来设置文件描述中的标志,相关代码如下:

	flags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flags | FASYNC);

先用F_GETFL命令取出文件描述符fd的标志,然后再利用命令F_SETFL添加标志FASYNC

通过在文件描述符 fd 上添加标志 FASYNC,文件描述符被配置为支持异步通知功能。

当设备发生事件时,驱动程序可以通过调用 kill_fasync() 函数,向设置了异步通知的文件描述符所属进程发送信号(默认是 SIGIO),比如下面的在驱动程序中的代码:

fasync_helper(fd, file, on, &button_fasync) //将文件描述符及文件结构体中的相关信息放入button_fasync中
......
kill_fasync(&button_fasync, SIGIO, POLL_IN); // 向设置了异步通知的文件描述符所属进程发送信号

另外还要特别注意代码fcntl(fd, F_SETFL, flags | FASYNC);会触发驱动程序中文件操作结构体中的成员fasync指向的函数gpio_key_drv_fasync()的调用,就像用户空间中的read函数会调用驱动程序中的read操作函数一样:

在这里插入图片描述
用户空间主要与fasync(文件异步通知机制)的代码就介绍完毕了,由于上面介绍的最后一句代码fcntl(fd, F_SETFL, flags | FASYNC);会触发驱动程序中函数gpio_key_drv_fasync的调用,所以我们就直接来看驱动程序中的函数gpio_key_drv_fasync

函数gpio_key_drv_fasync的代码如下:

struct fasync_struct *button_fasync;static int gpio_key_drv_fasync(int fd, struct file *file, int on)
{if (fasync_helper(fd, file, on, &button_fasync) >= 0)return 0;elsereturn -EIO;
}

这段代码定义了一个 fasync 文件操作函数,用于处理文件描述符的异步通知配置(FASYNC 标志的启用和禁用)。以下是详细解释:


代码逐步解析

  1. button_fasync

    struct fasync_struct *button_fasync;
    
    • 定义:这是一个指向 struct fasync_struct 的指针,代表一个链表,用于存储启用了异步通知的文件描述符信息。
    • 作用:当设备有事件需要通知用户空间时,驱动会使用这个链表,通过 kill_fasync() 函数向所有启用了异步通知的进程发送信号。
  2. 函数定义

    static int gpio_key_drv_fasync(int fd, struct file *file, int on)
    
    • 函数名gpio_key_drv_fasync,是驱动程序的 fasync 文件操作函数。
    • 参数
      • fd:文件描述符,由用户空间程序传递。
      • file:指向与文件描述符关联的 struct file 结构体。
      • on:一个布尔值,表示是否启用(非零值)或禁用(零值)异步通知。
    • 返回值
      • 成功返回 0
      • 失败返回 -EIO
  3. 调用 fasync_helper

    if (fasync_helper(fd, file, on, &button_fasync) >= 0)return 0;
    elsereturn -EIO;
    
    • fasync_helper() 的作用
      • 这是内核提供的一个辅助函数,用于管理异步通知链表。
      • 根据 on 的值:
        • 如果 on != 0,将 file 添加到 button_fasync 链表。
        • 如果 on == 0,从 button_fasync 链表中移除 file
      • 如果成功操作链表,返回非负值;否则返回负值。
    • 代码逻辑
      • 如果 fasync_helper() 成功(返回值 ≥ 0),函数返回 0
      • 如果失败(返回值 < 0),函数返回错误码 -EIO

工作原理

  1. 启用异步通知

    • 当用户空间调用 fcntl(fd, F_SETFL, flags | FASYNC) 时:
      • 内核会调用驱动的 gpio_key_drv_fasync() 函数,并将 on 参数设为非零值。
      • 通过 fasync_helper(),将文件描述符关联的 file 结构体加入到 button_fasync 链表。
    • 当设备事件发生时,驱动可以调用:
      kill_fasync(&button_fasync, SIGIO, POLL_IN);
      
      • 向用户空间的目标进程发送 SIGIO 信号,通知事件发生。
  2. 禁用异步通知

    • 当用户空间调用 fcntl(fd, F_SETFL, flags & ~FASYNC) 时:
      • 内核会调用 gpio_key_drv_fasync() 函数,并将 on 参数设为零。
      • 通过 fasync_helper(),将文件描述符关联的 filebutton_fasync 链表中移除。
    • 此后,设备事件将不再触发异步通知。

总结

  • gpio_key_drv_fasync 是驱动中负责管理异步通知的关键函数
  • 它通过 fasync_helper()
    • 添加或移除文件描述符到异步通知链表。
  • 应用场景
    • 异步事件通知:设备(如按键)触发事件后,通过 SIGIO 信号通知用户空间程序。
  • 核心机制
    • button_fasync 链表存储启用了异步通知的文件描述符。
    • 驱动调用 kill_fasync() 遍历链表并发送信号。

上面这段介绍把驱动程序中的中断处理函数gpio_key_isr中的代码:

kill_fasync(&button_fasync, SIGIO, POLL_IN);

也介绍了,这里就不多赘述了,只是要注意下第3个参数POLL_IN实际上没有啥实际作用,它的主要作用就是使代码增加了语义信息,使开发者可以快速理解该信号与“可读事件”相关。

至些,整个工程中与“fasync(文件异步通知机制)发送信号”的部分就解析完毕,从整个流程来看,还是比较简单和简洁的。

设备文件的编写

和下面这篇博文一样:
https://blog.csdn.net/wenhao_ir/article/details/145225508

Makfile文件内容

# 使用不同的Linux内核时, 一定要修改KERN_DIR,KERN_DIR代表已经配置、编译好的Linux源码的根目录KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) M=`pwd` modules$(CROSS_COMPILE)gcc -o button_test_03 button_test.cclean:make -C $(KERN_DIR) M=`pwd` cleanrm -rf modules.orderrm -f button_test_02obj-m += gpio_key_drv.o

交叉编译

工程目录复制到Ubuntu中。
在这里插入图片描述

make

把生成的目录文件复制到NFS网络文件目录中,备用。
在这里插入图片描述

加载驱动程序模块

打开串口终端→打开开发板→挂载网络文件系统

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
insmod  /mnt/read_key_irq_signal/gpio_key_drv.ko

在这里插入图片描述

查看有没有相应的设备文件生成

ls /dev/

有了,如下图所示:
在这里插入图片描述

运行测试程序

cd /mnt/read_key_irq_signal/
./button_test_03 /dev/read_keys0

运行后,按下按键,结果如下图所示:
在这里插入图片描述
测试结果表明,虽然测试程序的主程序中在不断循环运行打印信息的语句时,当它收到信号时,仍能去执行信号处理函数。

这就说明程序测试通过。

附完整工程文件

https://pan.baidu.com/s/11vGSz0AknsHGlVx8YfY4Hw?pwd=j2xs

相关文章:

使用Linux驱动程序的fasync(文件异步通知机制)向用户空间发送SIGIO信号的学习记录

前言 本文学习使用Linux驱动程序的fasync(文件异步通知机制)向用户空间发送SIGIO信号。 fasync(文件异步通知机制)名字的来历 fasync 是 “file asynchronous” 的缩写&#xff0c;意思是 文件异步通知。 这里的文件是指文件结构体struct file *file &#xff0c;关于文件结…...

面试经验分享-回忆版某小公司

说说你项目中数据仓库是怎么分层的&#xff0c;为什么要分层&#xff1f; 首先是ODS层&#xff0c;连接数据源和数据仓库&#xff0c;数据会进行简单的ETL操作&#xff0c;数据来源通常是业务数据库&#xff0c;用户日志文件或者来自消息队列的数据等 中间是核心的数据仓库层&a…...

【算法学习笔记】35:扩展欧几里得算法求解线性同余方程

线性同余方程问题 线程同余方程问题是指 a x ≡ b ( m o d m ) ax \equiv b~(mod~m) ax≡b (mod m)&#xff0c;给定 a a a、 b b b和 m m m&#xff0c;找到一个整数 x x x使得该方程成立&#xff0c;即使得 a x m o d m b ax~mod~mb ax mod mb&#xff0c;随便返回任何一个…...

ent.SetDatabaseDefaults()

在 AutoCAD 的 .NET API 中&#xff0c;ent.SetDatabaseDefaults() 这句代码通常用于将一个实体&#xff08;Entity&#xff09;对象的属性设置为与其所在的数据库&#xff08;Database&#xff09;的默认设置相匹配。这意味着&#xff0c;该实体将采用数据库级别的默认颜色、图…...

使用docker部署tomcat服务器和mysql数据库

使用docker部署tomcat服务器 1、拉去tomcat镜像 [rootlocalhost yum.repos.d]# sudo docker pull docker.io/tomcat:9 9: Pulling from library/tomcat de44b265507a: Pull complete 4c2afd91a87d: Pull complete 89e9bbcfa697: Pull complete 11be3e613582: Pull complet…...

Jenkins 启动

废话 这一阵子感觉空虚&#xff0c;心里空捞捞的&#xff0c;总想找点事情做&#xff0c;即使这是一件微小的事情&#xff0c;空余时间除了骑车、打球&#xff0c;偶尔朋友聚会 … 还能干什么呢&#xff1f; 当独自一人时&#xff0c;究竟可以做点什么&#xff0c;填补这空虚…...

Elasticsearch(ES)基础查询语法的使用

1. Match Query (全文检索查询) 用于执行全文检索&#xff0c;适合搜索文本字段。 { “query”: { “match”: { “field”: “value” } } } match_phrase&#xff1a;精确匹配短语&#xff0c;适合用于短语搜索。 { “query”: { “match_phrase”: { “field”: “text” }…...

SpringCloud系列教程:微服务的未来(十四)网关登录校验、自定义过滤器GlobalFilter、GatawayFilter

前言 在微服务架构中&#xff0c;API 网关扮演着至关重要的角色&#xff0c;负责路由请求、执行安全验证、流量控制等任务。Spring Cloud Gateway 作为一个强大的网关解决方案&#xff0c;提供了灵活的方式来实现这些功能。 本篇博客将重点介绍如何在 Spring Cloud Gateway 中…...

Android Studio:Linux环境下安装与配置

更多内容&#xff1a;XiaoJ的知识星球 Android Studio&#xff1a;Linux环境下安装与配置 1.安装JDK2.安装Android Studio2.1 获取安装包2.2 安装&#xff08;1&#xff09;配置环境变量&#xff1a;&#xff08;2&#xff09;运行安装&#xff1a;&#xff08;3&#xff09;配…...

使用AI生成金融时间序列数据:解决股市场的数据稀缺问题并提升信噪比

“GENERATIVE MODELS FOR FINANCIAL TIME SERIES DATA: ENHANCING SIGNAL-TO-NOISE RATIO AND ADDRESSING DATA SCARCITY IN A-SHARE MARKET” 论文地址&#xff1a;https://arxiv.org/pdf/2501.00063 摘要 金融领域面临的数据稀缺与低信噪比问题&#xff0c;限制了深度学习在…...

【银河麒麟高级服务器操作系统】业务访问慢网卡丢包现象分析及处理过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;product.kylinos.cn 开发者专区&#xff1a;developer.kylinos.cn 文档中心&#xff1a;document.kylinos.cn 交流论坛&#xff1a;forum.kylinos.cn 服务器环境以及配置 【内核版本…...

如何将数据库字符集改为中文,让今后所有的数据库都支持中文

最后一行有我自己的my.ini文件 数据库输入中文数据时会变为乱码&#xff0c; 这个时候&#xff0c;我们为每个数据库设置字符集&#xff0c;太过于麻烦&#xff0c;为数据库单独设置重启后又会消失 Set character_set_database’utf8’; Set character_set_server’utf8’; …...

Linux-C/C++--深入探究文件 I/O (下)(文件共享、原子操作与竞争冒险、系统调用、截断文件)

经过上一章内容的学习&#xff0c;了解了 Linux 下空洞文件的概念&#xff1b;open 函数的 O_APPEND 和 O_TRUNC 标志&#xff1b;多次打开同一文件&#xff1b;复制文件描述符&#xff1b;等内容 本章将会接着探究文件IO&#xff0c;讨论如下主题内容。  文件共享介绍&…...

Linux Bash 中使用重定向运算符的 5 种方法

注&#xff1a;机翻&#xff0c;未校。 Five ways to use redirect operators in Bash Posted: January 22, 2021 | by Damon Garn Redirect operators are a basic but essential part of working at the Bash command line. See how to safely redirect input and output t…...

opengrok_windows_环境搭建

目录 软件列表 软件安装 工程索引 ​编辑 工程部署 问题列表 软件列表 软件名下载地址用途JDKhttps://download.java.net/openjdk/jdk16/ri/openjdk-1636_windows-x64_bin.zipindex 使用java工具tomcathttps://dlcdn.apache.org/tomcat/tomcat-9/v9.0.98/bin/apache-tom…...

【无法下载github文件】虚拟机下ubuntu无法拉取github文件

修改hosts来进行解决。 步骤一&#xff1a;打开hosts文件 sudo vim /etc/hosts步骤二&#xff1a;查询 github.com的ip地址 https://sites.ipaddress.com/github.com/#ipinfo将github.com的ip地址添加到hosts文件末尾&#xff0c;如下所示。 140.82.114.3 github.com步骤三…...

python——句柄

一、概念 句柄指的是操作系统为了标识和访问对象而提供的一个标识符&#xff0c;在操作系统中&#xff0c;每个对象都有一个唯一的句柄&#xff0c;通过句柄可以访问对象的属性和方法。例如文件、进程、窗口等都有句柄。在编程中&#xff0c;可以通过句柄来操作这些对象&#x…...

.Net Core微服务入门系列(一)——项目搭建

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…...

Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…...

[苍穹外卖] 1-项目介绍及环境搭建

项目介绍 定位&#xff1a;专门为餐饮企业&#xff08;餐厅、饭店&#xff09;定制的一款软件产品 功能架构&#xff1a; 管理端 - 外卖商家使用 用户端 - 点餐用户使用 技术栈&#xff1a; 开发环境的搭建 整体结构&#xff1a; 前端环境 前端工程基于 nginx 运行 - Ngi…...

【PCIe 总线及设备入门学习专栏 2 -- PCIe 的 LTSSM 和 Enumeration】

文章目录 OverviewLTSSM StatesDetect StatesDETECT_QUIETDETECT_ACTDETECT_WAITPolling StatesPOLL_ACTIVEPOLL_CONFIGPOLL_COMPLIANCEConfiguration StatesCONFIG_LINKWD_STARTCONFIG_LINKWD_ACCEPTCONFIG_LANENUM_WAITCONFIG_LANENUM_ACCEPTCONFIG_COMPLETECONFIG_IDLERecov…...

Redis 性能优化:多维度技术解析与实战策略

文章目录 1 基准性能2 使用 slowlog 优化耗时命令3 big key 优化4 使用 lazy free 特性5 缩短键值对的存储长度6 设置键值的过期时间7 禁用耗时长的查询命令8 使用 Pipeline 批量操作数据9 避免大量数据同时失效10 客户端使用优化11 限制 Redis 内存大小12 使用物理机而非虚拟机…...

QT开发技术 【基于TinyXml2的对类进行序列化和反序列化】一

一、对TinyXml2 进行封装 使用宏 实现序列化和反序列化 思路&#xff1a; 利用宏增加一个类函数&#xff0c;使用序列化器调用函数进行序列化 封装宏示例 #define XML_SERIALIZER_BEGIN(ClassName) \ public: \virtual void ToXml(XMLElement* parentElem, bool bSerialize …...

麦田物语学习笔记:创建TransitionManager控制人物场景切换

基本流程 制作场景之间的切换 1.代码思路 (1)为了实现不同场景切换,并且保持当前的persistentScene一直存在,则需要一个Manager去控制场景的加载和卸载,并且在加载每一个场景之后,都要将当前的场景Set Active Scene,保证其为激活的场景,在卸载的时候也可以方便调用当前激活的场…...

2025年最新汽车零部件企业销售项目管理解决方案

在汽车零部件企业&#xff0c;销售项目管理的不规范和销售预测的不准确性常导致生产计划无法及时调整&#xff0c;因此客户关系常常中断&#xff0c;导致企业业务机会的丧失。为解决该问题&#xff0c;企业需要投入更多资源以优化销售流程与销售预测。 1、360多维立体客户视图…...

创建基于Prism框架的WPF应用(NET Framework)项目

创建基于Prism框架的WPF应用&#xff08;NET Framework&#xff09;项目 1、创建WPF&#xff08;NET Framework&#xff09;项目并整理结构 &#xff08;1&#xff09;、创建WPF&#xff08;NET Framework&#xff09;项目&#xff1b; &#xff08;2&#xff09;、添加Views和…...

AIGC视频生成模型:Meta的Emu Video模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍Meta的视频生成模型Emu Video&#xff0c;作为Meta发布的第二款视频生成模型&#xff0c;在视频生成领域发挥关键作用。 &#x1f33a;优质专栏回顾&am…...

【PowerQuery专栏】PowerQuery提取XML数据

XML数据和Json 数据类型都是比较典型的层次数据类型,XML的数据格式非常的对称。所有的数据均是由标签对组成,图为典型的XML文件类型的数据。 在PowerQuery中进行XML数据类型解析采用的是Xml.Document 函数来进行文件内容的解析,Xml.Document 目前有三个可用参数。 参数1为数…...

流行的开源高性能数据同步工具 - Apache SeaTunnel 整体架构运行原理

概述 背景 数据集成在现代企业的数据治理和决策支持中扮演着至关重要的角色。随着数据源的多样化和数据量的迅速增长&#xff0c;企业需要具备强大的数据集成能力来高效地处理和分析数据。SeaTunnel通过其高度可扩展和灵活的架构&#xff0c;帮助企业快速实现多源数据的采集、…...

基于单片机的多功能蓝牙语音智能台灯(论文+源码)

1总体方案设计 通过需求分析&#xff0c;本设计多功能蓝牙语音智能台灯的系统框图如图2.1所示&#xff0c;系统架构包括主控制器STM32F103单片机、HC-06蓝牙通信模块、LU-ASR01语音识别模块、OLED液晶、LED灯、按键等器件&#xff0c;在使用时用户可以通过手机APP、语音识别、…...

leetcode刷题记录(七十二)——146. LRU 缓存

&#xff08;一&#xff09;问题描述 146. LRU 缓存 - 力扣&#xff08;LeetCode&#xff09;146. LRU 缓存 - 请你设计并实现一个满足 LRU (最近最少使用) 缓存 [https://baike.baidu.com/item/LRU] 约束的数据结构。实现 LRUCache 类&#xff1a; * LRUCache(int capacity)…...

力扣203题(3)

题目及之前的两种解法大家可以移步到这里&#xff1a; https://blog.csdn.net/suibiansa_/article/details/145242573?spm1001.2014.3001.5501 力扣203题—— 移除链表元素-CSDN博客 今天呢我们来写一下第三种解法&#xff1a; 虚拟创建一个头结点 ListNode firstnew Lis…...

网络安全:信息时代的守护者

随着互联网的快速发展&#xff0c;网络安全问题日益成为全球关注的焦点。无论是个人用户、企业组织还是政府部门&#xff0c;网络安全都已成为保障信息安全、保护隐私、确保社会秩序的基石。在这个数字化时代&#xff0c;如何应对复杂多变的网络安全威胁&#xff0c;成为了我们…...

【博客之星2024年度总评选】年度回望:我的博客之路与星光熠熠

【个人主页】Francek Chen 【人生格言】征途漫漫&#xff0c;惟有奋斗&#xff01; 【热门专栏】大数据技术基础 | 数据仓库与数据挖掘 | Python机器学习 文章目录 前言一、个人成长与盘点&#xff08;一&#xff09;机缘与开端&#xff08;二&#xff09;收获与分享 二、年度创…...

接口自动化测试

APITEST: 接口自动化测试 目录结构介绍&#xff1a; conf目录&#xff1a;用来存放项目运行环境、执行环境相关的配置参数 testsuite目录&#xff1a;测试用例在此目录编写&#xff0c;pytest默认约定test开头的文件和方法为测试用例&#xff0c;不满足条件的不会被执行&#x…...

题海拾贝:力扣 138.随机链表的复制

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…...

第7章:Python TDD测试Franc对象乘法功能

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…...

PyTorch基本功能与实现代码

PyTorch是一个开源的深度学习框架&#xff0c;提供了丰富的函数和工具&#xff0c;以下为其主要功能的归纳&#xff1a; 核心数据结构&#xff1a; • 张量&#xff08;Tensor&#xff09;&#xff1a;类似于Numpy的ndarray&#xff0c;是PyTorch中基本的数据结构&#xff0c…...

【2024 CSDN博客之星】技术洞察类:从DeepSeek-V3的成功,看MoE混合专家网络对深度学习算法领域的影响(MoE代码级实战)

目录 一、引言 1.1 本篇文章侧重点 1.2 技术洞察—MoE&#xff08;Mixture-of-Experts&#xff0c;混合专家网络&#xff09; 二、MoE&#xff08;Mixture-of-Experts&#xff0c;混合专家网络&#xff09; 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场…...

Single-Model and Any-Modality for Video Object Tracking——2024——cvpr-阅读笔记

Single-Model and Any-Modality for Video Object Tracking 摘要相关工作创新处MethodShared embeddingModal promptingRGB Tracker based on TransformerOverall ExperiimentDatasetRGB-D samples are sourced from DepthTrackRGB-T samples are extracted from LasHeRRGB-E s…...

Java中的构造器

Java中的构造器详解 1. 什么是构造器 构造器&#xff08;Constructor&#xff09; 是一种特殊的方法&#xff0c;用于在创建对象时初始化对象的状态。构造器的名字必须与类名相同&#xff0c;且没有返回类型&#xff0c;连 void 也不能使用。 2. 构造器的特点 名称与类名相同…...

Restormer: Efficient Transformer for High-Resolution Image Restoration解读

论文地址&#xff1a;Restormer: Efficient Transformer for High-Resolution Image Restoration。 摘要 由于卷积神经网络&#xff08;CNN&#xff09;在从大规模数据中学习可推广的图像先验方面表现出色&#xff0c;这些模型已被广泛应用于图像复原及相关任务。近年来&…...

将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(3.纯python的实惠版)

前情&#xff1a; 将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch&#xff08;1.标准版&#xff09;-CSDN博客 将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch&#xff08;2.换掉付费的Event Hubs&#xff09;-CSDN博客 python脚本实现 厉害的…...

成就与远见:2024年技术与思维的升华

个人主页&#xff1a;chian-ocean 前言: 2025年1月17日&#xff0c;2024年博客之星年度评选——创作影响力评审的入围名单公布。我很荣幸能够跻身Top 300&#xff0c;虽然与顶尖博主仍有一定差距&#xff0c;但这也为我提供了更加明确的发展方向与指引。展望崭新的2025年&…...

BGP分解实验·9——路由聚合与条件性通告(1)

路由聚合是有效控制缩减BGP路由表的方法之一&#xff0c;路由聚合的前提和IGP一样&#xff0c;需要有路由目标存在BGP表中&#xff0c;与IGP不同的是&#xff0c;BGP路由聚合可以定义按需抑制路由的能力。 实验拓扑如下所示&#xff1a; 现在开始把从R1的R5的基础配置先准备好…...

栈和队列(C语言)

目录 数据结构之栈 定义 实现方式 基本功能实现 1&#xff09;定义&#xff0c;初始化栈 2&#xff09;入栈 3&#xff09;出栈 4&#xff09;获得栈顶元素 5)获得栈中有效元素个数 6&#xff09;检测栈是否为空 7&#xff09;销毁栈 数据结构之队列 定义 实现方…...

Jenkins-获取build用户信息

需求&#xff1a; 代码发布后&#xff0c;将发布结果发送至相关运维同学邮箱&#xff0c;需要获取发布人的信息。jenkins默认是没有相关内置变量的。 需要通过插件的方式进行解决&#xff1a; 插件&#xff1a; user build vars plugin 部署后&#xff0c;可使用的变量&…...

大数据学习(37)- Flink运行时架构

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…...

opengrok_windows_多工程环境搭建

目录 多工程的目录 工程代码下载和log配置 工程的索引 工程部署 工程测试 参考列表 多工程的目录 工程代码下载和log配置 工程代码下载 在每个工程的src目录下&#xff0c;下载工程代码&#xff0c;以下载pulseaudio的代码为例。 git clone gitgithub.com…...

Python网络自动化运维---SSH模块

目录 SSH建立过程 实验环境准备 一.SSH模块 1.1.Paramiko模块 1.1.1实验代码 1.1.2代码分段讲解 1.1.3代码运行过程 1.2Netmiko模块 Netmiko模块对比paramiko模块的改进&#xff1a; 1.2.1实验代码 1.2.2代码分段讲解 1.2.3代码运行过程 二.Paramiko模块和Ne…...