嵌入式Linux串口和 poll() 函数的使用

一、poll() 函数的介绍

在这里插入图片描述

  poll() 函数用于监控多个文件描述符的变化的函数。它可以用来检查一个或多个文件描述符的状态是否改变,比如是否可读、可写或有错误发生。它常用于处理 I/O 多路复用,这在需要同时处理多个网络连接或文件操作时非常有用。

头文件

#include <poll.h>  

函数原型

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

fds:一个指向 pollfd 结构数组的指针,每个 pollfd 结构代表一个要监控的文件描述符。
nfds:fds 数组中的元素数量。
timeout:等待的超时时间(毫秒)。如果设置为 -1,则 poll() 会一直等待,直到某个文件描述符的状态改变或捕获到信号。如果设置为 0,则 poll() 会立即返回,不会等待。
struct pollfd 结构

struct pollfd {  
   int fd;          // 文件描述符  
   short events;    // 感兴趣的事件  
   short revents;   // 返回的事件  
};

fd:要监控的文件描述符。
events:用户感兴趣的事件,可以是以下值的组合:
POLLIN:数据可读
POLLOUT:数据可写
POLLPRI:优先级数据可读
POLLERR:发生错误
POLLHUP:挂起(只用于流)
POLLNVAL:请求无效的文件描述符
revents:返回时,这个字段包含了实际发生的事件。
返回值
成功时,返回更改状态的文件描述符数量。
如果超时,返回 0。
失败时,返回 -1 并设置 errno。
示例
下面是一个简单的 poll() 使用示例,它监控标准输入(stdin)是否可读:

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <poll.h>  
  
int main() {  
    struct pollfd fds[1];  
    int ret;  
  
    // 设置要监控的文件描述符和事件  
    fds[0].fd = STDIN_FILENO; // 标准输入的文件描述符  
    fds[0].events = POLLIN;   // 监控读事件  
  
    // 使用 poll 监控文件描述符  
    ret = poll(fds, 1, -1); // 无限等待,直到有事件发生  
    if (ret == -1) {  
        perror("poll");  
        exit(EXIT_FAILURE);  
    }  
  
    // 检查哪个文件描述符的状态发生了改变  
    if (fds[0].revents & POLLIN) {  
        printf("Standard input is readable.\n");  
        // 这里可以读取标准输入的数据  
    }  
  
    return 0;  
}

在这里插入图片描述

  这个示例程序会等待用户从标准输入(通常是键盘)输入数据。一旦有数据可读,poll() 就会返回,并设置 fds[0].revents 为 POLLIN,表示标准输入现在可读。然后程序会打印一条消息。注意,这个示例并没有实际读取输入数据,只是检测了输入是否可读。如果需要读取数据,你可以使用如 read() 或 fgets() 等函数来从标准输入读取数据。

二、串口使用poll()函数测试代码

  测试代码基于文章 <嵌入式Linux开发板测试esp8266模块> 进行修改测试,文章链接如下:
http://t.csdnimg.cn/y31It
修改后代码如下:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <sys/select.h>
#include <poll.h>


#define REV_OK		0	//接收完成标志
#define REV_WAIT	1	//接收未完成标志
#define UART_BUF    128 // 串口缓冲区

#define ESP8266_AP_INFO         "AT+CWSAP=\"ATK-8266\",\"12345678\",1,4\r\n"
#define ESP8266_JAP_INFO          "AT+CWJAP=\"TP-LINK_xxxx\",\"123456789\"\r\n"
#define ESP8266_TCP_SERVER_INFO "AT+CIPSTART=\"TCP\",\"192.168.1.182\",8080\r\n"

#define u8 unsigned char

unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
unsigned char buf[UART_BUF] = {0};

/* 实现ms级延时 */
static void delay_xms(unsigned int secs)
{
    struct timeval tval;
    tval.tv_sec=secs/1000;
    tval.tv_usec=(secs*1000)%1000000;
    select(0,NULL,NULL,NULL,&tval);
}


typedef struct uart_hardware_cfg {
    unsigned int baudrate;  /* 波特率 */
    unsigned char dbit;     /* 数据位 */
    char parity;    /* 奇偶校验 */
    unsigned char sbit; /* 停止位 */
}uart_cfg_t;

static struct termios old_cfg; /* 用于保存终端的配置参数 */
static int fd; /* 串口终端对应的文件描述符 */

/* 串口初始化 
打开串口文件描述符,即对应的串口终端的设备节点 */
static int uart_init(const char *device)
{
    /* 打开串口 */
    /* O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机 */
    fd = open(device, O_RDWR | O_NOCTTY); 
    if (fd < 0)
    {
        fprintf(stderr, "open error:%s:%s\n", device, strerror(errno));
        return -1;
    }

    /* 获取串口当前的配置参数 */
    if (0 > tcgetattr(fd, &old_cfg)) {
        fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));
        close(fd);
        return -1;
    }

    return 0;
}

/**
 ** 串口配置
 ** 参数cfg指向一个uart_cfg_t结构体对象
 **/
static int uart_cfg(const uart_cfg_t *cfg)
{
    struct termios new_cfg = {0};   //将new_cfg对象清零
    speed_t speed;

    /* 设置为原始模式 */
    cfmakeraw(&new_cfg);

    /* 使能接收 */
    new_cfg.c_cflag |= CREAD;

    /* 设置波特率 */
    switch (cfg->baudrate) {
    case 1200: speed = B1200;
        break;
    case 1800: speed = B1800;
        break;
    case 2400: speed = B2400;
        break;
    case 4800: speed = B4800;
        break;
    case 9600: speed = B9600;
        break;
    case 19200: speed = B19200;
        break;
    case 38400: speed = B38400;
        break;
    case 57600: speed = B57600;
        break;
    case 115200: speed = B115200;
        break;
    case 230400: speed = B230400;
        break;
    case 460800: speed = B460800;
        break;
    case 500000: speed = B500000;
        break;
    default:    //默认配置为115200
        speed = B115200;
        printf("default baud rate: 115200\n");
        break;
    }

    if (0 > cfsetspeed(&new_cfg, speed)) {
        fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));
        return -1;
    }

    /* 设置数据位大小 */
    new_cfg.c_cflag &= ~CSIZE;  //将数据位相关的比特位清零
    switch (cfg->dbit) {
    case 5:
        new_cfg.c_cflag |= CS5;
        break;
    case 6:
        new_cfg.c_cflag |= CS6;
        break;
    case 7:
        new_cfg.c_cflag |= CS7;
        break;
    case 8:
        new_cfg.c_cflag |= CS8;
        break;
    default:    //默认数据位大小为8
        new_cfg.c_cflag |= CS8;
//        printf("default data bit size: 8\n");
        break;
    }

    /* 设置奇偶校验 */
    switch (cfg->parity) {
    case 'N':       //无校验
        new_cfg.c_cflag &= ~PARENB;
        new_cfg.c_iflag &= ~INPCK;
        break;
    case 'O':       //奇校验
        new_cfg.c_cflag |= (PARODD | PARENB);
        new_cfg.c_iflag |= INPCK;
        break;
    case 'E':       //偶校验
        new_cfg.c_cflag |= PARENB;
        new_cfg.c_cflag &= ~PARODD; /* 清除PARODD标志,配置为偶校验 */
        new_cfg.c_iflag |= INPCK;
        break;
    default:    //默认配置为无校验
        new_cfg.c_cflag &= ~PARENB;
        new_cfg.c_iflag &= ~INPCK;
//        printf("default parity: N\n");
        break;
    }

    /* 设置停止位 */
    switch (cfg->sbit) {
    case 1:     //1个停止位
        new_cfg.c_cflag &= ~CSTOPB;
        break;
    case 2:     //2个停止位
        new_cfg.c_cflag |= CSTOPB;
        break;
    default:    //默认配置为1个停止位
        new_cfg.c_cflag &= ~CSTOPB;
//        printf("default stop bit size: 1\n");
        break;
    }

    /* 将MIN和TIME设置为0 */
    new_cfg.c_cc[VTIME] = 0;
    new_cfg.c_cc[VMIN] = 0;

    /* 清空缓冲区 */
    if (0 > tcflush(fd, TCIOFLUSH)) {
        fprintf(stderr, "tcflush error: %s\n", strerror(errno));
        return -1;
    }

    /* 写入配置、使配置生效 */
    if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) {
        fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));
        return -1;
    }

    /* 配置OK 退出 */
    return 0;
}


//==========================================================
//	函数名称:	ESP8266_Clear
//
//	函数功能:	清空缓存
//
//	入口参数:	无
//
//	返回参数:	无
//
//	说明:		
//==========================================================
void ESP8266_Clear(void)
{
	memset(buf, 0, sizeof(buf));
	esp8266_cnt = 0;
}

/**
 ** 信号处理函数,当串口有数据可读时,会跳转到该函数执行
 **/
static void io_handler(int sig, siginfo_t *info, void *context)
{
    if(SIGRTMIN != sig)
        return;

    /* 判断串口是否有数据可读 */
    if (POLL_IN == info->si_code) 
    {
            
        if (esp8266_cnt >= sizeof(buf))
        {
            // esp8266_cnt = 0; //防止串口被刷爆
            // memset(buf,0,sizeof(buf));
            ESP8266_Clear();
        }
        esp8266_cnt = read(fd, buf+esp8266_cnt, sizeof(buf)-esp8266_cnt);
        printf("esp8266_cnt=%d\r\n", esp8266_cnt);
        printf("io_handler=%s\r\n", buf);
    }
}

/**
 ** 异步I/O初始化函数
 **/
static void async_io_init(void)
{
    struct sigaction sigatn;
    int flag;

    /* 使能异步I/O */
    flag = fcntl(fd, F_GETFL);  //使能串口的异步I/O功能
    flag |= O_ASYNC;
    fcntl(fd, F_SETFL, flag);

    /* 设置异步I/O的所有者 */
    fcntl(fd, F_SETOWN, getpid());

    /* 指定实时信号SIGRTMIN作为异步I/O通知信号 */
    fcntl(fd, F_SETSIG, SIGRTMIN);

    /* 为实时信号SIGRTMIN注册信号处理函数 */
    sigatn.sa_sigaction = io_handler;   //当串口有数据可读时,会跳转到io_handler函数
    sigatn.sa_flags = SA_SIGINFO;
    sigemptyset(&sigatn.sa_mask);
    sigaction(SIGRTMIN, &sigatn, NULL);
}

void Uart4_Init(unsigned int baud, char *device)
{
    uart_cfg_t cfg = {0};

    if (NULL == device) {
        fprintf(stderr, "Error: the device no found!\n");
        exit(EXIT_FAILURE);
    }

    /* 串口初始化 */
    if (uart_init(device))
        exit(EXIT_FAILURE);

    /* 设置波特率 */
    cfg.baudrate = baud;

    /* 串口配置 */
    if (uart_cfg(&cfg)) {
        tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置
        close(fd);
        exit(EXIT_FAILURE);
    }
}

void Uart5_Init(unsigned int baud, char *device)
{
    uart_cfg_t cfg = {0};

    if (NULL == device) {
        fprintf(stderr, "Error: the device no found!\n");
        exit(EXIT_FAILURE);
    }

    /* 串口初始化 */
    if (uart_init(device))
        exit(EXIT_FAILURE);

    /* 设置波特率 */
    cfg.baudrate = baud;

    /* 串口配置 */
    if (uart_cfg(&cfg)) {
        tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置
        close(fd);
        exit(EXIT_FAILURE);
    }
}



//==========================================================
//	函数名称:	ESP8266_WaitRecive
//
//	函数功能:	等待接收完成
//
//	入口参数:	char *res
//
//	返回参数:	REV_OK-接收完成		REV_WAIT-接收超时未完成
//
//	说明:		循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(char *res)
{
    int err=0,nbytes=0,i=0;
    struct pollfd fds[] = {
        {
            .fd	= fd,
            .events	= POLLIN,
        },
    };
    err = poll(fds, 1, 5000);//5s
    switch(err)
    {
        case -1://出错
            printf("\n poll read error =%d \n", err);
            nbytes=0;
            break;
        case 0://超时
            printf("\n poll read error =%d \n", err);
            nbytes=0;
            break;
        default:
            nbytes = read(fd, buf+esp8266_cnt, sizeof(buf)-esp8266_cnt);

            break;
    }
    esp8266_cnt += nbytes;
	if(esp8266_cnt == 0) 							//如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
		return REV_WAIT;
		
	if( strstr((const char *)buf, res) != NULL || esp8266_cnt == esp8266_cntPre)				//如果上一次的值和这次相同,则说明接收完毕
	{

        // for (i = 0; i < esp8266_cnt; i++)
        //     printf(" 0x%02x", buf[i]);

        // printf("\n");
        printf("***********************\n");
        printf("%s",buf);
        printf("***********************\n");

		esp8266_cnt = 0;							//清0接收计数
        esp8266_cntPre=0;
			
		return REV_OK;								//返回接收完成标志
	}
		
	esp8266_cntPre = esp8266_cnt;					//置为相同
	
	return REV_WAIT;								//返回接收未完成标志

}

int ESP8266_Recive(void)
{
    int err=0,nbytes=0,i=0;
    struct pollfd fds[] = {
        {
            .fd	= fd,
            .events	= POLLIN,
        },
    };
    memset(buf,0,sizeof(buf));
    err = poll(fds, 1, 1000);//1s
    switch(err)
    {
        case -1://出错
            printf("\n poll read error =%d \n", err);
            nbytes=0;
            break;
        case 0://超时
            printf("\n poll read error =%d \n", err);
            nbytes=0;
            break;
        default:
            nbytes = read(fd, buf, sizeof(buf));
            break;
    }
    if(nbytes>0)
    {
        return nbytes;
    }
    return -1;

}

//==========================================================
//	函数名称:	ESP8266_SendCmd
//
//	函数功能:	发送命令
//
//	入口参数:	cmd:命令
//				res:需要检查的返回指令
//
//	返回参数:	0-成功	1-失败
//
//	说明:		
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
	int ret = 0,err,nbytes;
	unsigned int timeOutCnt = 3;
    ESP8266_Clear();
	ret = write(fd, (unsigned char *)cmd, strlen((const char *)cmd));
	if (ret == 0)   printf("write err!\r\n");
//	printf("UartBuf:%s, Len:%d\r\n", buf, esp8266_cnt);

    while(timeOutCnt--)
	{
		if(ESP8266_WaitRecive(res) == REV_OK)							//如果收到数据
		{
//			printf("ESP8266_SendCmd Function:%s\r\n", buf);
            if(strstr((const char *)buf, res) != NULL)		//如果接收到的数据是在给定数据的范围内,则不为NULL
			{
				
                ESP8266_Clear();									//清空缓存
				
				return 0;
			}
		}
        //delay_xms(10);//10s内让串口2中断函数循环接收到的数据
	}
	
	return 1;

}

//==========================================================
//	函数名称:	ESP8266_SendData
//
//	函数功能:	发送数据
//
//	入口参数:	data:数据
//				len:长度
//
//	返回参数:	无
//
//	说明:		
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{

	char cmdBuf[32];
	
	ESP8266_Clear();								//清空接收缓存
	// sprintf(cmdBuf, "AT+CIPSEND=0,%d\r\n", len);		//发送命令  多连接
    sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len);		//发送命令  单连接
	if(!ESP8266_SendCmd(cmdBuf, ">"))				//收到‘>’时可以发送数据,改不得
	{
		write(fd, data, len);		//发送设备连接请求数据
	}

}

_Bool Esp8266_Init()
{
    ESP8266_Clear();

	printf("1. AT\r\n");
	while(ESP8266_SendCmd("AT\r\n", "OK"))
			delay_xms(500);
	
	printf("2. CWMODE\r\n");
	while(ESP8266_SendCmd("AT+CWMODE=2\r\n", "OK"))
			delay_xms(500);
	
	printf("2.1 AT+RST\r\n");
	while(ESP8266_SendCmd("AT+RST\r\n", "OK"))
			delay_xms(2000);

	printf("3. CWSAP\r\n");
	while(ESP8266_SendCmd(ESP8266_AP_INFO, "OK"))
	{
		delay_xms(500);
	}

    printf("4. AT+CIPMUX\r\n");
	// 启动多连接
	while(ESP8266_SendCmd("AT+CIPMUX=1\r\n", "OK"))
	{
		delay_xms(500);
	}

	printf("5. CIPSERVER\r\n");
	while(ESP8266_SendCmd("AT+CIPSERVER=1,8080\r\n", "OK"))
			delay_xms(500);

    printf("6. CIFSR\r\n");
	while(ESP8266_SendCmd("AT+CIFSR\r\n", "OK"))
			delay_xms(500);
	
	printf("6. ESP8266 Init OK\r\n");
    return 0;
}
_Bool Esp8266_Init_One_TCP_Client()
{
    ESP8266_Clear();
	printf("1. CWMODE\r\n");
	while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
			delay_xms(5000);
    // printf("1.1 CWDHCP\r\n");
	// while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
	// 		delay_xms(5000);
    printf("1.1 CIPSTA\r\n");
	while(ESP8266_SendCmd("AT+CIPSTA=\"192.168.1.87\",\"192.168.1.1\",\"255.255.255.0\"\r\n", "OK"))
			delay_xms(5000);
            //
	printf("2. CWJAP\r\n");
	while(ESP8266_SendCmd(ESP8266_JAP_INFO, "OK"))//WIFI CONNECTED ;// OK
		delay_xms(5000);
    // printf("2.1 AT+RST\r\n");
	// while(ESP8266_SendCmd("AT+RST\r\n", "OK"))
	// 		delay_xms(20000);
	
    printf("3. CIFSR\r\n");
	while(ESP8266_SendCmd("AT+CIFSR\r\n", "OK"))
			delay_xms(5000);

    printf("4. CIPSTART\r\n");
	while(ESP8266_SendCmd(ESP8266_TCP_SERVER_INFO, "ERROR")==0)
			delay_xms(5000*10);
	
	printf("4. ESP8266 Init OK\r\n");
    return 0;
}

int main(int argc, char *argv[])
{
    u8 test_buf[] = "esp8266 $$$$$!\r\n";
    // Uart4_Init(115200, argv[1]); // 初始化串口4
    Uart5_Init(115200, argv[1]); // 初始化串口5
    //async_io_init(); // 异步IO初始化,相当于STM32的外部中断配置的初始化,不过STM32下应该是硬件中断,这里是软件中断 
    // while (Esp8266_Init())
    // {
    //     printf("Esp8266 Init Failed!!!\r\n");
    // }
    while (Esp8266_Init_One_TCP_Client())
    {
        printf("Esp8266 Init Failed!!!\r\n");
    }

    while (1)
    {
        if(ESP8266_Recive() == -1)
        {
            sleep(1); 
            continue;
        }
        /* 通信测试 */
        if (strstr((const char*)buf, "AA"))
        {
            ESP8266_SendData(test_buf,strlen(test_buf));
            ESP8266_Clear();
        }
        else if(strstr((const char*)buf, "end"))
        {
            sleep(1); 
            ESP8266_Clear();
            break;
        }
            
    }
    
    /* 退出 */
    tcsetattr(fd, TCSANOW, &old_cfg);   //恢复到之前的配置
    close(fd);
    exit(EXIT_SUCCESS);
}

三、测试结果

初始化部分

1. CWMODE
***********************
AT+CWMODE=1

OK
***********************
1.1 CIPSTA
***********************
AT+CIPSTA="192.168.1.87","192.168.1.1","255.255.255.0"

OK
***********************
2. CWJAP
***********************
AT+CWJAP="TP-LINK_xxxx","123456789"
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IP

OK
***********************
3. CIFSR
***********************
AT+CIFSR
+CIFSR:STAIP,"192.168.1.87"
+CIFSR:STAMAC,"34:94:54:7f:d4:71"

OK
***********************
4. CIPSTART

 poll read error =0
***********************
AT+CIPSTART="TCP","192.168.1.182",8080
CONNECT

OK
***********************
4. ESP8266 Init OK

主函数循环接收,接收到AA,返回 esp8266 $$$$$!,接收到end,则结束。
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux高级IO之select

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目录 一、五种IO模型 1.IO效率的问题 2.阻塞IO是…

蓝桥杯C/C++实用知识总结

蓝桥杯C/C 文章目录 蓝桥杯C/C头文件实用函数及运算符求幂次移位运算符STL排序sort()函数依次读取数据STL全排列函数next_permutation()求数组最大/最小值初始化函数memset()GCD(最大公约数)和LCM&#xff08;最小公倍数&#xff09;C字符串函数 实用数据结构模板vector链表lis…

未来城市:探索数字孪生在智慧城市中的实际应用与价值

目录 一、引言 二、数字孪生与智慧城市的融合 三、数字孪生在智慧城市中的实际应用 1、智慧交通管理 2、智慧能源管理 3、智慧建筑管理 4、智慧城市管理 四、数字孪生在智慧城市中的价值 五、挑战与展望 六、结论 一、引言 随着科技的飞速发展&#xff0c;智慧城市已…

鸿蒙OpenHarmony HDF 驱动开发

目录 序一、概述二、HDF驱动框架三、驱动程序四、驱动配置坚持就有收获 序 最近忙于适配OpenHarmonyOS LiteOS-M 平台&#xff0c;已经成功实践适配平台GD32F407、STM32F407、STM32G474板卡&#xff0c;LiteOS适配已经算是有实际经验了。 但是&#xff0c;鸿蒙代码学习进度慢下…

超网、IP 聚合、IP 汇总分别是什么?三者有啥区别和联系?

一、超网 超网&#xff08;Supernet&#xff09;是一种网络地址聚合技术&#xff0c;它可以将多个连续的网络地址合并成一个更大的网络地址&#xff0c;从而减少路由表的数量和大小。超网技术可以将多个相邻的网络地址归并成一个更大的网络地址&#xff0c;这个更大的网络地址…

Lesson 6 Convolutional Neural Network(CNN)

听课&#xff08;李宏毅老师的&#xff09;笔记&#xff0c;方便梳理框架&#xff0c;以作复习之用。本节课主要讲了CNN的适用范围&#xff0c;整体架构与工作流程&#xff0c;CNN的应用&#xff0c;CNN的缺点以及解决方法。 1. CNN的输入与输出 CNN是专门为了图像而设计的一…

2.4_3 死锁的处理策略——避免死锁

文章目录 2.4_3 死锁的处理策略——避免死锁&#xff08;一&#xff09;什么是安全序列&#xff08;二&#xff09;安全序列、不安全状态、死锁的联系&#xff08;三&#xff09;银行家算法 总结 2.4_3 死锁的处理策略——避免死锁 银行家算法是“避免死锁”策略的最著名的一个…

【sgExcelGrid】自定义组件:简单模拟Excel表格拖拽、选中单元格、横行、纵列、拖拽圈选等操作

特性&#xff1a; 可以自定义拖拽过表格可以点击某个表格&#xff0c;拖拽右下角小正方形进行任意方向选取单元格支持选中某一行、列支持监听selectedGrids、selectedDatas事件获取选中项的DOM对象和数据数组支持props自定义显示label字段别名 sgExcelGrid源码 <template&g…

LLM长上下文外推方法

现在的LLM都集中在卷上下文长度了&#xff0c;最新的Claude3已经支持200K的上下文&#xff0c;见&#xff1a;cost-context。下面是一些提升LLM长度外推能力的方法总结&#xff1a; 数据工程 符尧大佬的最新工作&#xff1a;Data Engineering for Scaling Language Models to …

计算机网络——计算机网络的性能

计算机网络——计算机网络的性能 速率带宽吞吐量时延时延宽带积往返时间RTT利用率信道利用率网络利用率 我们今天来看看计算机网络的性能。 速率 速率这个很简单&#xff0c;就是数据的传送速率&#xff0c;也称为数据率&#xff0c;或者比特率&#xff0c;单位为bit/s&#…

C语言——强制类型转化

强制类型转化的作用 C语言中的强制类型转换是一种将一个数据类型转换为另一个数据类型的操作。它可以通过显式地指定要转换的数据类型来实现。强制类型转换可以用于以下几种情况&#xff1a; 改变变量的数据类型&#xff1a;当需要将一个变量的数据类型从一种类型转换为另一种…

【libwebrtc】基于m114

libwebrtc A C++ wrapper for binary release, mainly used for flutter-webrtc desktop (windows, linux, embedded).是 基于m114版本的webrtc 最新(20240309 ) 的是m122了。官方给出的构建过程 .gclient 文件 solutions = [{"name" : src,"url

域名交易系统已测试可正常使用免授权带后台

域名交易系统已测试可正常使用免授权带后台 下载地址&#xff1a;迅雷云盘

python处理geojson为本地shp文件

一.成果展示 二.环境 我是在Anaconda下的jupyter notebook完成代码的编写&#xff0c;下面是我对应的版本号&#xff0c;我建议大家在这个环境下编写&#xff0c;因为在下载gdal等包的时候会更方便。 二.参考网站 osgeo.osr module — GDAL documentation osgeo.ogr module …

链表基础知识详解

链表基础知识详解 一、链表是什么&#xff1f;1.链表的定义2.链表的组成3.链表的优缺点4.链表的特点 二、链表的基本操作1.链表的建立2.链表的删除3.链表的查找4.链表函数 一、链表是什么&#xff1f; 1.链表的定义 链表是一种物理存储单元上非连续、非顺序的存储结构&#xf…

SQLite3中的callback回调函数注意的细节

调用 sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)该例程提供了一个执行 SQL 命令的快捷方式&#xff0c; SQL 命令由 sql 参数提供&#xff0c;可以由多个 SQL 命令组成。 在这里&#xff0c; 第一个参数 sqlite3 是打开的数据库对…

Go语言数据结构(二)堆/优先队列

文章目录 1. container中定义的heap2. heap的使用示例3. 刷lc应用堆的示例 更多内容以及其他Go常用数据结构的实现在这里&#xff0c;感谢Star&#xff1a;https://github.com/acezsq/Data_Structure_Golang 1. container中定义的heap 在golang中的"container/heap"…

使用yarn创建vite+vue3electron多端运行

文章目录 第一步 使用yarn创建vite+vue3项目遇到创建报错看第二步 引入electron第三步 创建main.js在electron下面的main.js写入下面代码第四步 安装同时运行多条命令npm包&&修改package.json文件npm包增加一条electron运行脚本命令效果图第一步 使用yarn创建vite+vue3…

T-RAG = RAG + Fine-Tuning + Entity Detection

原文地址&#xff1a;T-RAG RAG Fine-Tuning Entity Detection T-RAG 方法的前提是将 RAG 架构与开源微调的 LLM 和实体树向量数据库相结合。重点是上下文检索。 2024 年 2 月 15 日 介绍 大型语言模型 (LLM) 越来越多地应用于各个领域&#xff0c;包括对私营企业文档的问答…

Pb量级超大容量光存储

近日&#xff0c;中国科学院上海光学精密机械研究所&#xff08;以下简称“上海光机所”&#xff09;与上海理工大学等科研单位合作&#xff0c;在超大容量三维超分辨光存储研究中取得突破性进展。研究团队利用国际首创的双光束调控聚集诱导发光超分辨光存储技术&#xff0c;实…