【Linux-阻塞,非阻塞,异步】

Linux-阻塞,非阻塞,异步

  • ■ Linux-阻塞-非阻塞 IO-异步
  • ■ Linux-阻塞IO
    • ■ 阻塞IO简介
    • ■ open
    • ■ 等待队列
    • ■ 示例一:APP阻塞IO读取进入睡眠后-等待队列唤醒进程
  • ■ Linux-非阻塞IO
    • ■ 非阻塞IO简介
    • ■ open
    • ■ 轮询
      • ■ 1、select 函数
      • ■ 2、poll 函数
      • ■ 3、epoll 函数
  • ■ 异步通知-信号
    • ■ 示例一:CTRL+C 组合键以后先在终端上打印出“SIGINT signal!”
    • ■ 驱动中的信号处理
      • ■ 1、 fasync_struct 结构体
      • ■ 2、 fasync 函数
      • ■ 3、 kill_fasync 函数

■ Linux-阻塞-非阻塞 IO-异步

在这里插入图片描述

■ Linux-阻塞IO

■ 阻塞IO简介

在这里插入图片描述

■ open

fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞方式打开 */

■ 等待队列

进程阻塞访问IO-》进程阻塞进入休眠-》等待队列-》 唤醒进程。
阻塞访问最大的好处就是当设备文件不可操作的时候进程可以进入休眠态,这样可以将CPU 资源让出来。
但是,当设备文件可以操作的时候就必须唤醒进程,一般在中断函数里面完成唤醒工作。

Linux 内核提供了等待队列(wait queue)来实现阻塞进程的唤醒工作。
等待队列头使用结构体wait_queue_head_t 表示, wait_queue_head_t 结构体定义在文件 include/linux/wait.h 中,

  1. 等待队列头
struct __wait_queue_head {spinlock_t lock;struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;void init_waitqueue_head(wait_queue_head_t *q)  //q 就是要初始化的等待队列头。
  1. 等待队列项
struct __wait_queue {unsigned int flags;void *private;wait_queue_func_t func;struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;DECLARE_WAITQUEUE(name, tsk)  //初始化一个等待队列项name 就是等待队列项的名字, tsk 表示这个等待队列项属于哪个任务(进程),一般设置为current 
  1. 将队列项添加/移除等待队列头
    当设备不可访问的时候就需要将进程对应的等待队列项添加到前面创建的等待队列头中,只有添加到等待队列头中以后进程才能进入休眠态。
    当设备可以访问以后再将进程对应的等待队列项从等待队列头中移除即可.
void add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)q: 等待队列项要加入的等待队列头。wait:要加入的等待队列项。void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)q: 要删除的等待队列项所处的等待队列头。wait:要删除的等待队列项。
  1. 等待唤醒
    当设备可以使用的时候就要唤醒进入休眠态的进程。这两个函数会将这个等待队列头中的所有进程都唤醒。
void wake_up(wait_queue_head_t *q)     //可以唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 状态的进程
void wake_up_interruptible(wait_queue_head_t *q)  //只能唤醒处于 TASK_INTERRUPTIBLE 状态的进程。q 就是要唤醒的等待队列头
  1. 等待事件
    除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程
1 wait_event(wq, condition)
等待以 wq 为等待队列头的等待队列被唤醒,前提是 condition 条件必须满足(为真),否则一直阻塞 。
此 函 数 会 将 进 程 设 置 为TASK_UNINTERRUPTIBLE 状态
TASK_UNINTERRUPTIBLE状态是一种不可中断的睡眠状态,不可以被信号打断,必须等到等待的条件满足时才被唤醒。2 wait_event_timeout(wq, condition, timeout)
功能和 wait_event 类似,但是此函数可以添加超时时间,以 jiffies 为单位。此函数有返回值,
如果
返回 0 的话表示超时时间到,而且 condition为假。
返回 1 的话表示 condition 为真,也就是条件满足了。3 wait_event_interruptible(wq, condition)
与 wait_event 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断。4 wait_event_interruptible_timeout(wq,condition, timeout)
与 wait_event_timeout 函数类似,此函数也将进程设置为 TASK_INTERRUPTIBLE,可以被信号打断。

■ 示例一:APP阻塞IO读取进入睡眠后-等待队列唤醒进程

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define IMX6UIRQ_CNT		1			/* 设备号个数 	*/
#define IMX6UIRQ_NAME		"blockio"	/* 名字 		*/
#define KEY0VALUE			0X01		/* KEY0按键值 	*/
#define INVAKEY				0XFF		/* 无效的按键值 */
#define KEY_NUM				1			/* 按键数量 	*//* 中断IO描述结构体 */
struct irq_keydesc {int gpio;								/* gpio */int irqnum;								/* 中断号     */unsigned char value;					/* 按键对应的键值 */char name[10];							/* 名字 */irqreturn_t (*handler)(int, void *);	/* 中断服务函数 */
};/* imx6uirq设备结构体 */
struct imx6uirq_dev{dev_t devid;			/* 设备号 	 */	struct cdev cdev;		/* cdev 	*/                 struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */int major;				/* 主设备号	  */int minor;				/* 次设备号   */struct device_node	*nd; /* 设备节点 */	atomic_t keyvalue;		/* 有效的按键键值 */atomic_t releasekey;	/* 标记是否完成一次完成的按键,包括按下和释放 */struct timer_list timer;/* 定义一个定时器*/struct irq_keydesc irqkeydesc[KEY_NUM];	/* 按键init述数组 */unsigned char curkeynum;				/* 当前init按键号 */wait_queue_head_t r_wait;	/* 读等待队列头 */
};struct imx6uirq_dev imx6uirq;	/* irq设备 *//* @description		: 中断服务函数,开启定时器		*				  	  定时器用于按键消抖。* @param - irq 	: 中断号 * @param - dev_id	: 设备结构。* @return 			: 中断执行结果*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));	/* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}/* @description	: 定时器服务函数,用于按键消抖,定时器到了以后*				  再次读取按键值,如果按键还是处于按下状态就表示按键有效。* @param - arg	: 设备结构变量* @return 		: 无*/
void timer_function(unsigned long arg)
{unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio); 	/* 读取IO值 */if(value == 0){ 						/* 按下按键 */atomic_set(&dev->keyvalue, keydesc->value);}else{ 									/* 按键松开 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1);	/* 标记松开按键,即完成一次完整的按键过程 */}               /* 唤醒进程 */if(atomic_read(&dev->releasekey)) {	/* 完成一次按键过程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);}
}/** @description	: 按键IO初始化* @param 		: 无* @return 		: 无*/
static int keyio_init(void)
{unsigned char i = 0;char name[10];int ret = 0;imx6uirq.nd = of_find_node_by_path("/key");if (imx6uirq.nd== NULL){printk("key node not find!\r\n");return -EINVAL;} /* 提取GPIO */for (i = 0; i < KEY_NUM; i++) {imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd ,"key-gpio", i);if (imx6uirq.irqkeydesc[i].gpio < 0) {printk("can't get key%d\r\n", i);}}/* 初始化key所使用的IO,并且设置成中断模式 */for (i = 0; i < KEY_NUM; i++) {memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name));	/* 缓冲区清零 */sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);		/* 组合名字 */gpio_request(imx6uirq.irqkeydesc[i].gpio, name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);	imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i);
#if 0imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif}/* 申请中断 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for (i = 0; i < KEY_NUM; i++) {ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}/* 创建定时器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;/* 初始化等待队列头 */init_waitqueue_head(&imx6uirq.r_wait);return 0;
}/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int imx6uirq_open(struct inode *inode, struct file *filp)
{filp->private_data = &imx6uirq;	/* 设置私有数据 */return 0;
}/** @description     : 从设备读取数据 * @param - filp    : 要打开的设备文件(文件描述符)* @param - buf     : 返回给用户空间的数据缓冲区* @param - cnt     : 要读取的数据长度* @param - offt    : 相对于文件首地址的偏移* @return          : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;#if 0/* 加入等待队列,等待被唤醒,也就是有按键按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;} 
#endifDECLARE_WAITQUEUE(wait, current);	        /* 定义一个等待队列 */if(atomic_read(&dev->releasekey) == 0) {	/* 没有按键按下 */add_wait_queue(&dev->r_wait, &wait);	/* 将等待队列添加到等待队列头 */__set_current_state(TASK_INTERRUPTIBLE);/* 设置任务状态 */schedule();							    /* 进行一次任务切换 */if(signal_pending(current))	{			/* 判断是否为信号引起的唤醒 */ret = -ERESTARTSYS;goto wait_error;}__set_current_state(TASK_RUNNING);         /* 将当前任务设置为运行状态 */remove_wait_queue(&dev->r_wait, &wait);    /* 将对应的队列项从等待队列头删除 */}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if (releasekey) { /* 有按键按下 */	if (keyvalue & 0x80) {keyvalue &= ~0x80;ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));} else {goto data_error;}atomic_set(&dev->releasekey, 0);/* 按下标志清零 */} else {goto data_error;}return 0;wait_error:set_current_state(TASK_RUNNING);		/* 设置任务为运行态 */remove_wait_queue(&dev->r_wait, &wait);	/* 将等待队列移除 */return ret;data_error:return -EINVAL;
}/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,
};/** @description	: 驱动入口函数* @param 		: 无* @return 		: 无*/
static int __init imx6uirq_init(void)
{/* 1、构建设备号 */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注册字符设备 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);/* 3、创建类 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) {	return PTR_ERR(imx6uirq.class);}/* 4、创建设备 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、始化按键 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit imx6uirq_exit(void)
{unsigned i = 0;/* 删除定时器 */del_timer_sync(&imx6uirq.timer);	/* 删除定时器 *//* 释放中断 */	for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);gpio_free(imx6uirq.irqkeydesc[i].gpio);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class);
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");	

阻塞访问测试APP

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;int ret = 0;char *filename;unsigned char data;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, &data, sizeof(data));if (ret < 0) {  /* 数据读取错误或者无效 */} else {		/* 数据读取正确 */if (data)	/* 读取到数据 */printf("key value = %#X\r\n", data);}}close(fd);return ret;
}

■ Linux-非阻塞IO

■ 非阻塞IO简介

在这里插入图片描述

■ open

fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); 
/* 非阻塞方式打开 表示以非阻塞方式打开设备,这样从设备中读取数据的时候就是非阻塞方式*/

■ 轮询

如果用户应用程序以非阻塞的方式访问设备,设备驱动程序就要提供非阻塞的处理方式,也就是轮询。 poll、 epoll 和 select 可以用于处理轮询,

■ 1、select 函数

int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout)nfds: 所要监视的这三类文件描述集合中, 最大文件描述符加 1。readfds、 writefds 和 exceptfds:这三个指针指向描述符集合,readfds 用于监视指定描述符集的读变化,也就是监视这些文件是否可以读取,只要这些集合里面有一个文件可以读取那么 seclect 就会返回一个大于 0 的值表示文件可以读取。如果没有文件可以读取,那么就会根据 timeout 参数来判断是否超时。可以将 readfs设置为 NULL,表示不关心任何文件的读变化exceptfds 用于监视这些文件的异常:void FD_ZERO(fd_set *set)          用于将 fd_set 变量的所有位都清零void FD_SET(int fd, fd_set *set)   用于将 fd_set 变量的某个位置 1,也就是向 fd_set 添加一个文件描述符,参数 fd 就是要加入的文件描述符void FD_CLR(int fd, fd_set *set)   用于将 fd_set 变量的某个位清零,也就是将一个文件描述符从 fd_set 中删除,参数 fd 就是要删除的文件描述符int FD_ISSET(int fd, fd_set *set)  用于测试一个文件是否属于某个集合,参数 fd 就是要判断的文件描述符。timeout:超时时间:struct timeval {long tv_sec; /* 秒 */long tv_usec; /* 微妙 */};

示例程序

 void main(void){int ret, fd; /* 要监视的文件描述符 */fd_set readfds; /* 读操作文件描述符集 */struct timeval timeout; /* 超时结构体 */fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */FD_ZERO(&readfds); /* 清除 readfds */FD_SET(fd, &readfds); /* 将 fd 添加到 readfds 里面 *//* 构造超时时间 */timeout.tv_sec = 0;timeout.tv_usec = 500000; /* 500ms */ret = select(fd + 1, &readfds, NULL, NULL, &timeout);switch (ret) {case 0: /* 超时 */printf("timeout!\r\n");break;case -1: /* 错误 */printf("error!\r\n");break;default: /* 可以读取数据 */if(FD_ISSET(fd, &readfds)) { /* 判断是否为 fd 文件描述符 *//* 使用 read 函数读取数据 */}break;}
}

■ 2、poll 函数

int poll(struct pollfd *fds,nfds_t nfds,int timeout)
fds: 要监视的文件描述符集合以及要监视的事件,为一个数组,数组元素都是结构体 pollfd类型的struct pollfd {int fd; /* 文件描述符 */short events; /* 请求的事件 */      events 是要监视的事件short revents; /* 返回的事件 */     revents 是返回参数,也就是返回的事件,};events: POLLIN 有数据可以读取。POLLPRI 有紧急的数据需要读取。POLLOUT 可以写数据。POLLERR 指定的文件描述符发生错误。POLLHUP 指定的文件描述符挂起。POLLNVAL 无效的请求。POLLRDNORM 等同于 POLLIN
nfds: poll 函数要监视的文件描述符数量。
timeout: 超时时间,单位为 ms。返回值:返回 revents 域中不为 0 的 pollfd 结构体个数,也就是发生事件或错误的文件描述符数量; 0,超时; -1,发生错误,并且设置 errno 为错误类型。

示例程序

void main(void)
{int ret;int fd; /* 要监视的文件描述符 */struct pollfd fds;fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式访问 *//* 构造结构体 */fds.fd = fd;fds.events = POLLIN; /* 监视数据是否可以读取 */ret = poll(&fds, 1, 500); /* 轮询文件是否可操作,超时 500ms */if (ret) { /* 数据有效 */....../* 读取数据 */......} else if (ret == 0) { /* 超时 */......} else if (ret < 0) { /* 错误 */......}
}

■ 3、epoll 函数

■ 异步通知-信号

异步通知的核心就是信号,在 arch/xtensa/include/uapi/asm/signal.h 文件中定义了 Linux 所支持的所有信号

#define SIGHUP 1 /* 终端挂起或控制进程终止 */
#define SIGINT 2 /* 终端中断(Ctrl+C 组合键) */
#define SIGQUIT 3 /* 终端退出(Ctrl+\组合键) */
#define SIGILL 4 /* 非法指令 */
#define SIGTRAP 5 /* debug 使用,有断点指令产生 */
#define SIGABRT 6 /* 由 abort(3)发出的退出指令 */
#define SIGIOT 6 /* IOT 指令 */
#define SIGBUS 7 /* 总线错误 */
#define SIGFPE 8 /* 浮点运算错误 */
#define SIGKILL 9 /* 杀死、终止进程 */
#define SIGUSR1 10 /* 用户自定义信号 1 */
#define SIGSEGV 11 /* 段违例(无效的内存段) */
#define SIGUSR2 12 /* 用户自定义信号 2 */
#define SIGPIPE 13 /* 向非读管道写入数据 */
#define SIGALRM 14 /* 闹钟 */
#define SIGTERM 15 /* 软件终止 */
#define SIGSTKFLT 16 /* 栈异常 */
#define SIGCHLD 17   /* 子进程结束 */

SIGKILL(9)和 SIGSTOP(19)这两个信号不能被忽略外,其他的信号都可以忽略。
这些信号就相当于中断号,不同的中断号代表了不同的中断。
驱动程序可以通过向应用程序发送不同的信号来实现不同的功能。

sighandler_t signal(int signum, sighandler_t handler)signum:要设置处理函数的信号。handler: 信号的处理函数。信号处理函数原型
typedef void (*sighandler_t)(int)

■ 示例一:CTRL+C 组合键以后先在终端上打印出“SIGINT signal!”

当按下键盘上的 CTRL+C 组合键以后会向当前正在占用终端的应用程序发出 SIGINT 信号, SIGINT 信号默认的动作是关闭当前应用程序。这里我们修改一下 SIGINT 信号的默认处理函数,
当按下 CTRL+C 组合键以后先在终端上打印出“SIGINT signal!”

#include "stdlib.h"
#include "stdio.h"
#include "signal.h"void sigint_handler(int num)
{printf("\r\nSIGINT signal!\r\n");exit(0);
}int main(void)
{signal(SIGINT, sigint_handler);while(1);return 0;
}

■ 驱动中的信号处理

■ 1、 fasync_struct 结构体

struct fasync_struct {spinlock_t fa_lock;int magic;int fa_fd;struct fasync_struct *fa_next;struct file *fa_file;struct rcu_head fa_rcu;
};

■ 2、 fasync 函数

int (*fasync) (int fd, struct file *filp, int on)
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

驱动中编写fasync 函数

struct xxx_dev {......struct fasync_struct *async_queue; /* 异步相关结构体 */
};static int xxx_fasync(int fd, struct file *filp, int on)
{struct xxx_dev *dev = (xxx_dev)filp->private_data;if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)return -EIO;return 0;
}static struct file_operations xxx_ops = {.......fasync = xxx_fasync,......
};

■ 3、 kill_fasync 函数

驱动程序需要向应用程序发出信号。

void kill_fasync(struct fasync_struct **fp, int sig, int band)fp:要操作的 fasync_struct。sig: 要发送的信号。band: 可读时设置为 POLL_IN,可写时设置为 POLL_OUT。

在这里插入代码片

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

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

相关文章

python-双胞胎字符串

[问题描述]&#xff1a;给定两个字符串s和t&#xff0c;每次可以任意交换s的奇数位和偶数位的字符&#xff0c;即奇数位的字符可以与任意其它奇数位的字符交换&#xff0c;偶数位的字符同样也可以与任意偶数位的字符的字符交换&#xff0c;问能否在有限的次数的交换下使s变为t?…

智能售货机加盟新契机

加盟智能售货机业务&#xff0c;尤其是在当前技术迭代迅速与市场需求高涨的背景下&#xff0c;正成为众多创业者积极探索的领域。以重庆臻奶惠为代表的企业&#xff0c;正利用其在智能科技与支付资讯的深厚底蕴&#xff0c;为有意涉足该行业的加盟者铺设了一条既稳健又具前瞻性…

2024年5月31日 (周五) 叶子游戏新闻

《Granblue Fantasy: Relink》版本更新 新增可操控角色及功能世嘉股份有限公司现已公开《Granblue Fantasy: Relink》&#xff08;以下简称 Relink&#xff09;免费版本更新ver.1.3.1于5月31日&#xff08;周五&#xff09;上线的消息。该作是由Cygames Inc.&#xff08;下称Cy…

【CTF Web】BUUCTF BUU LFI COURSE 1 Writeup(代码审计+PHP+文件包含漏洞)

BUU LFI COURSE 1 1 点击启动靶机。 解法 <?php /*** Created by PhpStorm.* User: jinzhao* Date: 2019/7/9* Time: 7:07 AM*/highlight_file(__FILE__);if(isset($_GET[file])) {$str $_GET[file];include $_GET[file]; }如果GET请求中接收到file参数&#xff0c;就会…

刷代码随想录有感(87):贪心算法——K次取反后的最大化数组和

题干&#xff1a; 代码&#xff1a; class Solution { public:static bool cmp(int a, int b){return abs(a) > abs(b);}int largestSumAfterKNegations(vector<int>& nums, int k) {sort(nums.begin(), nums.end(), cmp);for(int i 0; i < nums.size(); i){…

使用clip模型计算图文相似度

使用clip模型计算图文相似度 clip模型模型架构训练过程应用场景clip安装 计算图文相似度 clip模型 CLIP (Contrastive Language-Image Pretraining) 是由 OpenAI 开发的一种深度学习模型&#xff0c;旨在将自然语言处理和计算机视觉任务结合起来。它通过一种名为对比学习&…

Springboot+vue二手房交易管理系统

Springbootvue二手房交易管理系统&#xff0c;项目用mybatis与数据库&#xff0c;数据库为mysql&#xff0c;16个表结构。有问题直接加我询问&#xff0c;我会一直在线哒。 功能如下&#xff1a; 房东管理 用户管理 房源信息管理&#xff08;可预约和查看评论&#xff09; 看房…

【LLM第7篇】transformer跟bert、gpt、大模型的联系

上一篇讲了transformer的原理&#xff0c;接下来&#xff0c;看看它的衍生物们。 Transformer基本架构 Transformer模型主要由两部分组成&#xff1a;编码器&#xff08;Encoder&#xff09;和解码器&#xff08;Decoder&#xff09;。编码器负责处理输入序列&#xff0c;将其…

NKCTF 2024 webshell_pro

还是正常的HTTP流量 既然是webshell一定是看POST流量 对每一个进行追踪tcp流 最终发现 在 流9 (tcp.stream eq 9)存在 base32 -->base64的流量的加密逻辑 import base64import libnum from Crypto.PublicKey import RSApubkey """-----BEGIN PUBLIC KEY…

文刻创作ai工具在哪下载

文刻创作ai工具是一种基于人工智能技术开发的软件工具&#xff0c;旨在辅助用户进行文创作品的创作和编辑。 领取激活方式&#xff1a;https://qvfbz6lhqnd.feishu.cn/wiki/HsY4wmoffiNp4FkB2AbcpL4tn6d 该工具通过自然语言处理、机器学习等技术&#xff0c;可以生成具有一定创…

MyBatis源码分析--02:SqlSession建立过程

我们再来看看MyBatis使用流程&#xff1a; InputStream inputStream Resources.getResourceAsStream("myBatis_config.xml"); SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream); SqlSession session sqlSessionFactory.op…

Linux主机安全可视化运维(免费方案)

本文介绍如何使用免费的主机安全软件,在自有机房或企业网络实现对Linux系统进行可视化“主机安全”管理。 一、适用对象 本文适用于个人或企业内的Linux服务器运维场景,实现免费、高效、可视化的主机安全管理。提前发现主机存在的安全风险,全方位实时监控主机运行时入侵事…

【惯性传感器imu】—— WHEELTEC的惯导模块的imu的驱动安装配置和运行

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、IMU驱动安装1. 安装依赖2. 源码的下载3. 编译源码(1) 配置固定串口设备(2) 修改luanch文件(3) 编译 二、启动IMU1. 运行imu2. 查看imu数据 总结 前言 WHEE…

C#WPF数字大屏项目实战02--主窗体布局

1、主窗体起始属性 设置有关属性如下&#xff1a; WindowStyle"None"-》无边框 AllowsTransparency"True" -》允许透明 WindowStartupLocation"CenterScreen"-》启动时位于屏幕中间 FontFamily"Microsoft YaHei"-》字体微软雅黑 …

python编程:创建 SQLite 数据库和表的图形用户界面应用程序

在本文中&#xff0c;我将介绍如何使用 wxPython 模块创建一个图形用户界面&#xff08;GUI&#xff09;应用程序&#xff0c;该应用程序允许用户选择 SQLite 数据库的存放路径、数据库名称、表名称&#xff0c;并动态添加字段及其类型。以下是具体的实现步骤和代码示例。 C:\p…

Unity实现简单的持久化存储

在Unity中&#xff0c;运行过程中的内容是不会保存的&#xff0c;但是如果我们有些游戏数据需要持久化存储&#xff0c;应该怎么办呢&#xff0c;所以Unity为我们提供了一个简单的数据存储的API。 附上代码片段 //写入数据PlayerPrefs.SetInt("IntType", 1);PlayerPr…

FreeRTOS【13】流缓冲区使用

1.开发背景 基于以上的章节&#xff0c;了解了 FreeRTOS 多线程间的信号量、队列的使用&#xff0c;已经满足了日常使用场景。其中&#xff0c;队列的使用规定了队伍成员的大小&#xff0c;然而现实使用场景下&#xff0c;很多数据不都是定长大小了&#xff0c;例如不定长的通讯…

在AutoDL上部署Yi-34B大模型

在AutoDL上部署Yi-34B大模型 Yi介绍 Yi 系列模型是 01.AI 从零训练的下一代开源大语言模型。Yi 系列模型是一个双语语言模型&#xff0c;在 3T 多语言语料库上训练而成&#xff0c;是全球最强大的大语言模型之一。Yi 系列模型在语言认知、常识推理、阅读理解等方面表现优异。 …

一键AI抠图,证件照换背景,可部署成自己的应用

1 开发背景 AI抠图技术已经非常成熟&#xff0c;并且有效果非常好的开源模型。 日常中可以用于替换证件照背景 但是网上许多的证件照替换背景 竟然需要收费 鉴于此&#xff0c;便将目前最好的&#xff08;SOTA&#xff09;开源抠图模型 BRIA Background Removal v1.4 Model …

前端Vue自定义带加减按钮的数字输入框组件的设计与实现

随着前端技术的不断发展&#xff0c;开发的复杂度日益提升。传统的整块应用开发方式在面对小改动或小功能增加时&#xff0c;常常需要修改大量代码&#xff0c;导致整个系统的逻辑受到影响。为了解决这个问题&#xff0c;组件化开发成为了前端开发的必然趋势。 一、组件化开发…