【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.rhkb.cn/news/337004.html

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

相关文章

20240531在飞凌的OK3588-C开发板上跑原厂的Buildroot测试USB摄像头

20240531在飞凌的OK3588-C开发板上跑原厂的Buildroot测试USB摄像头 2024/5/31 20:04 USB摄像头分辨率&#xff1a;1080p&#xff08;1920x1080&#xff09; 默认编译Buildroot的SDK即可点亮USB摄像头。v4l2-ctl --list-devices v4l2-ctl --list-formats-ext -d /dev/video74 …

【mysql】ubuntu下安装数据库

1更新软件包 sudo apt update //更新软件包2安装数据库 sudo apt install mysql-server//安装数据库注意后面mysql-server是个整体 3安全设置配置 sudo mysql_secure_installation//安全设置配置你要设置验证密码吗&#xff1f; 输入设置密码安全等级&#xff08;0,1&am…

【PostgreSQL17新特性之-冗余IS [NOT] NULL限定符的处理优化】

在执行一个带有IS NOT NULL或者NOT NULL的SQL的时候&#xff0c;通常会对表的每一行&#xff0c;都会进行检查以确保列为空/不为空&#xff0c;这是符合常理的。 但是如果本身这个列上有非空&#xff08;NOT NULL&#xff09;约束&#xff0c;再次检查就会浪费资源。甚至有时候…

U盘损坏打不开?数据恢复攻略全解析

随着信息技术的飞速发展&#xff0c;U盘已成为我们日常工作和生活中不可或缺的数据存储工具。然而&#xff0c;当U盘突然损坏&#xff0c;无法打开时&#xff0c;我们往往会陷入焦虑和无助之中。本文将为大家详细解析U盘损坏打不开的原因&#xff0c;并提供两种有效的数据恢复方…

Git基本使用教程(学习记录)

参考文章链接&#xff1a; Git教程&#xff08;超详细&#xff0c;一文秒懂&#xff09; RUNOOB Git教程 Git学习记录 1Git概述 1.1版本控制软件功能 版本管理&#xff1a;更新或回退到历史上任何版本&#xff0c;数据备份共享代码&#xff1a;团队间共享代码&#xff0c;…

Gavin Wood 访谈|Polkadot 从何而来,又将如何面对 AI 时代?

如果没有宏观经济&#xff0c;加密世界可能无法存在。或许&#xff0c;Satoshi Nakamoto 也永远不会写出那篇开创性的白皮书。区块链技术作为指数时代的核心之一&#xff0c;在宏观经济理论中占有重要地位。传统的经济增长公式是人口增长加生产率增长加债务增长。然而&#xff…

Python导出Jira列表

import requests import urllib3 urllib3.disable_warnings() from jira import JIRA import pandas as pd def login_jira(username,password):jira JIRA("https://jira.cn/",basic_auth(username,password))#projectsjira.project(id13)# jqlproject"云链-…

3D轻量化的三大应用解决方案

老子云平台https://www.laozicloud.com/ 为不同应用场景提供了三大解决方案。 01 单模型轻量化解决方案 数字化时代&#xff0c;越来越多的C2M定制、文旅、电商等行业&#xff0c;为了开拓市场&#xff0c;提升企业竞争力&#xff0c;开始把目光投向产品的3D展示交互。 单模…

CSS 空间转换 动画

目录 1. 空间转换1.1 视距 - perspective1.2 空间转换 - 旋转1.3 立体呈现 - transform-style1.4 空间转换 - 缩放 2. 动画 - animation2.1 动画的基本用法2.1 animation 复合属性2.2 animation 拆分属性2.3 多组动画 正文开始 1. 空间转换 空间&#xff1a;是从坐标轴角度定义…

基于编译型语言鲲鹏应用开发小技巧

编译型语言应用执行过程 大部分应用可以通过重新编译即可移植到鲲鹏平台 预处理命令: gcc -E hello.c -o hello.i&#xff0c;预处理完成后使用命令: cat hello.i可以看到预处理后的代码 编译命令: gcc -s hello.i -o hello.s 汇编命令: gcc -c hello.c -o hello.o 链接处理…

pytorch学习笔记2

首先如果遇到conda找不到包&#xff0c;pip老是超时的情况建议添加一下镜像源 conda的 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ cond…

【ETAS CP AUTOSAR基础软件】EcuM模块详解

文章包含了AUTOSAR基础软件&#xff08;BSW&#xff09;中EcuM模块相关的内容详解。本文从AUTOSAR规范解析&#xff0c;ISOLAR-AB配置以及模块相关代码分析三个维度来帮读者清晰的认识和了解EcuM。文中涉及的SOLAR-AB配置以及模块相关代码都是依托于ETAS提供的工具链来配置与生…

Java集合【超详细】

文章目录 一、集合框架1.1 概述1.2 数组和集合的区别1.3 Java集合框架体系1.4 数据结构1.4.1 栈、队列、数组、队列1.4.2 二叉树【理解】1.4.3 二叉查找树【理解】1.4.4 平衡二叉树【理解】1.4.5 红黑树【理解】 1.5 泛型 二、Collection集合2.1 Collection 集合概述和使用【应…

如何利用智能算法降低成本,扩大收益?

算法交易&#xff08;Algorithm Trading&#xff09;是一种投资策略&#xff0c;它使用计算机算法来分析市场数据&#xff0c;制定交易决策&#xff0c;并自动执行交易。算法交易的主要目标是利用市场价格波动来获取利润&#xff0c;同时降低人为干预的风险和成本。 量化交易中…

【漏洞复现】WordPress Country State City Dropdown CF7插件 SQL注入漏洞(CVE-2024-3495)

0x01 产品简介 Country State City Dropdown CF7插件是一个功能强大、易于使用的 WordPress 插件&#xff0c;它为用户在联系表单中提供国家.州/省和城市的三级下拉菜单功能&#xff0c;帮助用户更准确地填写地区信息。同时&#xff0c;插件的团队和支持也非常出色&#xff0c…

MySQL 索引的使用

本篇主要介绍MySQL中索引使用的相关内容。 目录 一、最左前缀法则 二、索引失效的场景 索引列运算 字符串无引号 模糊查询 or连接条件 数据分布 一、最左前缀法则 当我们在使用多个字段构成的索引时&#xff08;联合索引&#xff09;&#xff0c;需要考虑最左前缀法则…

COMSOL中液晶材料光学特性模拟

前面我们根据FDTD官方文档设置了液晶指向的模型。COMSOL也可以根据相似的方法设置各项异性的周期性变化的材料。 该方法参考了luneburg_lens的COMSOL文档 在给出的文件中&#xff0c;可以发现定义-变量中可以使用默认坐标作为变量&#xff0c;即xyz。因此&#xff0c;折射率也可…

德人合科技——天锐绿盾内网安全管理软件 | -文档透明加密模块

天锐绿盾文档加密功能能够为各种模式的电子文档提供高强度加密保护&#xff0c;丰富的权限控制以及灵活的应用管理&#xff0c;帮助企业构建更严密的立体保密体系。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee ————…

【算法】MT2 棋子翻转

✨题目链接&#xff1a; MT2 棋子翻转 ✨题目描述 在 4x4 的棋盘上摆满了黑白棋子&#xff0c;黑白两色棋子的位置和数目随机&#xff0c;其中0代表白色&#xff0c;1代表黑色&#xff1b;左上角坐标为 (1,1) &#xff0c;右下角坐标为 (4,4) 。 现在依次有一些翻转操作&#…

【并发程序设计】12.内存映射

12.内存映射 使一个磁盘文件与内存中的一个缓冲区相映射&#xff0c;进程可以像访问普通内存一样对文件进行访问&#xff0c;不必再调用read,write&#xff0c;更加高效。 用到的函数 mmap函数 原型&#xff1a; #include <sys/mman.h> void* mmap(void* start, size_…