【网络编程开发】11.IO模型 12.IO多路复用
11.IO模型
什么是IO:
IO 是 Input/Output 的缩写,指的是输入和输出。在计算机当中,IO 操作通常指将数据从一个设备或文件中读取到计算机内存中,或将内存中的数据写入设备或文件中。这些设备可以包括硬盘驱动器、网卡、键盘、屏幕等。
通常用户进程中的一个完整I/O分为两个阶段:
用户进程空间→内核空间
内核空间→设备空间
I/O分为内存I/O、网络I/O和磁盘I/O三种
IO操作的两个阶段
Linux中进程无法直接操作I/O设备,其必须通过系统调用请求内核来协助完成I/O操作。
内核会为每个I/O设备维护一个缓冲区。
对于一个输入操作来说,进程I/O系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备(比如网卡设备)中读取(因为设备I/O一般速度较慢,需要等待);
内核缓冲区有数据则直接复制到用户进程空间。
所以,对于一个网络输入操作通常包括两个不同阶段:
- 等待网络数据到达网卡,把数据从网卡读取到内核缓冲区,准备好数据。
- 从内核缓冲区复制数据到用户进程空间。
网络I/O的本质是对socket的读取,socket在Linux系统中被抽象为流,I/O可以理解为对流的操作。
网络I/O的模型可分为两种:
- 异步I/O(asynchronous I/O)
- 同步I/O(synchronous I/O)
同步I/O又包括
- 阻塞I/O(blocking I/O)
- 非阻塞I/O(non-blocking I/O)
- 多路复用I/O(multiplexing I/O)
- 信号驱动I/O(signal-driven I/O)
强调一下:信号驱动I/O属于同步I/O,原因往后看。
信号驱动I/O和异步I/O只作概念性的讲解,不作为学习重点。
五种I/O模型
阻塞I/O(blocking I/O)
对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达,当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步是把数据从内核缓冲区复制到应用程序缓冲区。
同步阻塞I/O模型是最常用、最简单的模型。在Linux中,默认情况下,所有套接字都是阻塞的。下面我们以阻塞套接字的recvfrom的调用图来说明阻塞,如图所示
非阻塞I/O(non-blocking I/O)
非阻塞的recvform系统调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error
(EAGAIN
或EWOULDBLOCK
)。
进程在返回之后,可以先处理其他的业务逻辑,稍后再发起recvform系统调用。
采用轮询的方式检查内核数据,直到数据准备好。再拷贝数据到进程,进行数据处理。
在Linux下,可以通过设置套接字选项使其变为非阻塞。非阻塞的套接字的recvfrom操作如图所示
可以看到前三次调用recvfrom请求时,并没有数据返回,内核返回errno
(EWOULDBLOCK
),并不会阻塞进程。
当第四次调用recvfrom时,数据已经准备好了,于是将它从内核空间拷贝到程序空间,处理数据。
在非阻塞状态下,I/O执行的等待阶段并不是完全阻塞的,但是第二个阶段依然处于一个阻塞状态(调用者将数据从内核拷贝到用户空间,这个阶段阻塞)。
多路复用I/O(multiplexing I/O)
I/O多路复用的好处在于单个进程就可以同时处理多个网络连接的I/O。它的基本原理是不再由应用程序自己监视连接,而由内核替应用程序监视文件描述符。
以select函数为例,当用户进程调用了select,那么整个进程会被阻塞,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好,select就会返回。
这个时候用户进程再调用read操作,将数据从内核拷贝到用户进程,如下图所示。
信号驱动I/O(signal-driven I/O)
该模型允许socket进行信号驱动I/O,并注册一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据,如图所示
注意:虽然信号驱动IO在注册完信号处理函数以后,就可以做其他事情了。但是第二阶段拷贝数据的过程当中进程依然是被阻塞的,而后要介绍的异步IO是完全不会阻塞进程的,所以信号驱动虽然具有异步的特点,但依然属于异步IO
异步I/O(asynchronous I/O)
相对于同步I/O,异步I/O不是按顺序执行。用户进程进行aio_read
系统调用之后,就可以去处理其他逻辑了,无论内核数据是否准备好,都会直接返回给用户进程,不会对进程造成阻塞。这是因为aio_read
只向内核递交申请,并不关心有没有数据。
等到数据准备好了,内核直接复制数据到进程空间,然后内核向进程发送通知,此时数据已经在用户空间了,可以对数据进行处理。
五种I/O模型比较
前四种I/O模型都是同步I/O操作,它们的区别在于第一阶段,而第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。
相反,异步I/O模型在等待数据和接收数据的这两个阶段都是非阻塞的,可以处理其他的逻辑,用户进程将整个I/O操作交由内核完成,内核完成后会发送通知。在此期间,用户进程不需要检查I/O操作的状态,也不需要主动拷贝数据。
在了解了Linux的I/O模型之后,我们就可以进行服务器设计了。
12.IO多路复用
I/O多路复用的好处在于单个进程就可以同时处理多个网络连接的I/O。它的基本原理是不再由应用程序自己监视连接,而由内核替应用程序监视文件描述符。
在Linux系统中,select、poll和epoll是三种常用的I/O多路复用技术,它们用于处理多个I/O流,以实现高效的并发服务器设计。
特性 | select函数 | poll函数 | epoll函数族 |
---|---|---|---|
支持平台 | 几乎所有类Unix系统 | Unix及类Unix系统 | 主要为Linux系统 |
数据结构 | 位图(限制文件描述符数量) | 链表(无文件描述符数量限制) | 红黑树(高效管理事件) |
文件描述符限制 | 通常最多1024个 | 无限制 | 无限制 |
拷贝开销 | 每次调用时拷贝整个集合 | 每次调用时拷贝整个集合 | 通过回调机制,避免拷贝 |
返回就绪描述符 | 需要遍历所有描述符识别就绪状态 | 直接返回就绪状态的描述符 | 高效返回只包含就绪事件的描述符 |
并发性能 | 低至中等(因位图扫描) | 中等(因链表扫描) | 高(高效的事件通知和数据结构) |
触发模式 | 不支持 | 不支持 | 支持水平触发和边缘触发 |
API复杂度 | 简单直观 | 类似select但更灵活 | 功能丰富但使用相对复杂 |
适用场景 | 小规模并发服务器或客户端 | 中规模并发服务器 | 大规模并发服务器,尤其是网络服务 |
select函数
原型:
#include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:监视文件描述符集合,等待其中任意一个文件描述符准备好进行I/O操作。
参数:
nfds
:文件描述符集合中最大的文件描述符值加1。readfds
:需要监视可读状态的文件描述符集合。writefds
:需要监视可写状态的文件描述符集合。exceptfds
:需要监视异常状态的文件描述符集合。timeout
:设置select函数的超时时间。
- NULL,永久阻塞。
- 0,非阻塞。
返回值:
- 成功:返回准备好的文件描述符个数。
- 超时:返回0。
- 出错:返回-1,并设置errno。
fd_set
:
- 表示文件描述符集合的数据结构
- 在fd_set中,每个文件描述符都对应一个位,如果该位为1,则表示对应的文件描述符处于准备好的状态;如果该位为0,则表示对应的文件描述符未准备好。
- fd_set提供了一些宏操作来方便地对文件描述符集合进行操作:
FD_ZERO(fd_set *set)
:清空文件描述符集合。FD_SET(int fd, fd_set *set)
:将指定的文件描述符添加到集合中。FD_CLR(int fd, fd_set *set)
:从集合中移除指定的文件描述符。FD_ISSET(int fd, fd_set *set)
:检查指定的文件描述符是否在集合中。FD_COPY(fd_set *src, fd_set *dst)
:复制源文件描述符集合到目标文件描述符集合。
struct timeval
:
struct timeval {long tv_sec; /* 秒 */long tv_usec; /* 微秒 */ };
select实现多路复用
sever.c
#include "net.h"
#include <sys/select.h>
#define MAX_SOCK_FD 1024 // 定义最大文件描述符数量为1024int main(int argc, char *argv[])
{int i, ret, fd, newfd; fd_set set, tmpset; Addr_in clientaddr;socklen_t clientlen = sizeof(Addr_in); // 定义客户端地址长度clientlen/*检查参数,小于3个 直接退出进程*/Argment(argc, argv);/*创建已设置监听模式的套接字*/fd = CreateSocket(argv);FD_ZERO(&set);FD_ZERO(&tmpset);FD_SET(fd, &set);while(1){ tmpset = set;if( (ret = select(MAX_SOCK_FD, &tmpset, NULL, NULL, NULL)) < 0) // 调用select函数监听文件描述符集合tmpset中的文件描述符ErrExit("select"); if(FD_ISSET(fd, &tmpset) ){/*接收客户端连接,并生成新的文件描述符*/if( (newfd = accept(fd, (Addr *)&clientaddr, &clientlen) ) < 0) perror("accept");printf("[%s:%d]已建立连接\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); // 输出客户端地址和端口号FD_SET(newfd, &set); // 将新的文件描述符加入文件描述符集合set}else{ for(i = fd + 1; i < MAX_SOCK_FD; i++){ if(FD_ISSET(i, &tmpset)){ if( DataHandle(i) <= 0){ if( getpeername(i, (Addr *)&clientaddr, &clientlen) )perror("getpeername"); printf("[%s:%d]断开连接\n", inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port)); // 输出客户端地址和端口号FD_CLR(i, &set); // 从文件描述符集合set中移除文件描述符i}}}}}return 0; // 程序正常结束,返回0
}
socket.c
#include "net.h"void Argment(int argc, char *argv[]){if(argc < 3){fprintf(stderr, "%s<addr><port>\n", argv[0]);exit(0);}
}
int CreateSocket(char *argv[]){/*创建套接字*/int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd < 0)ErrExit("socket");/*允许地址快速重用*/int flag = 1;if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag) ) )perror("setsockopt");/*设置通信结构体*/Addr_in addr;bzero(&addr, sizeof(addr) );addr.sin_family = AF_INET;addr.sin_port = htons( atoi(argv[2]) );/*绑定通信结构体*/if( bind(fd, (Addr *)&addr, sizeof(Addr_in) ) )ErrExit("bind");/*设置套接字为监听模式*/if( listen(fd, BACKLOG) )ErrExit("listen");return fd;
}
int DataHandle(int fd){char buf[BUFSIZ] = {};Addr_in peeraddr;socklen_t peerlen = sizeof(Addr_in);if( getpeername(fd, (Addr *)&peeraddr, &peerlen) )perror("getpeername");int ret = recv(fd, buf, BUFSIZ, 0);if(ret < 0)perror("recv");if(ret > 0){printf("[%s:%d]data: %s\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);}return ret;
}
net.h
#ifndef _NET_H_
#define _NET_H_#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5
#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)void Argment(int argc, char *argv[]);
int CreateSocket(char *argv[]);
int DataHandle(int fd);#endif
成功建立连接
poll 函数
与select函数的功能类似
原型:
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:监视一组文件描述符的I/O状态,等待它们中的一个或多个变为可读、可写或异常状态。
参数:
fds
:一个指向pollfd
结构体数组的指针,该数组包含了需要监视的文件描述符及其对应的事件。nfds
:fds
数组中的元素个数。timeout
:等待的最长时间(以毫秒为单位),如果设置为0,则表示立即返回;如果设置为负数,则表示无限期等待。返回值:
- 如果成功,返回发生事件的文件描述符个数;
- 如果超时,返回0;
- 如果出错,返回-1。
struct pollfd
:
struct pollfd {int fd; // 文件描述符short events; // 注册的事件short revents; // 返回的事件 };
fd
:这是文件描述符,即需要被监视的句柄。
events
:这是一个位掩码,定义了我们关心的文件描述符的事件类型。常用的事件类型有:
POLLIN
:表示文件描述符可读。POLLOUT
:表示文件描述符可写。POLLPRI
:表示文件描述符有紧急数据(带外数据)可读。POLLERR
:表示文件描述符发生错误。POLLHUP
:表示文件描述符挂起。
revents
:这是一个输出参数,当poll返回时,它指出了文件描述符上实际发生了哪些事件。
nfds_t
:
typedef unsigned long int nfds_t;
poll实现多路复用
sever.c
#include "net.h"
#include <poll.h>#define MAX_SOCK_FD 1024
int main(int argc, char *argv[])
{int i, j, fd, newfd;nfds_t nfds = 1;struct pollfd fds[MAX_SOCK_FD] = {};Addr_in addr;socklen_t addrlen = sizeof(Addr_in);/*检查参数,小于3个 直接退出进程*/Argment(argc, argv);/*创建已设置监听模式的套接字*/fd = CreateSocket(argv);fds[0].fd = fd;fds[0].events = POLLIN;while(1){if( poll(fds, nfds, -1) < 0)ErrExit("poll");for(i = 0; i < nfds; i++){/*接收客户端连接,并生成新的文件描述符*/if(fds[i].fd == fd && fds[i].revents & POLLIN){if( (newfd = accept(fd, (Addr *)&addr, &addrlen) ) < 0)perror("accept");fds[nfds].fd = newfd;fds[nfds++].events = POLLIN;printf("[%s:%d][nfds=%lu] connection successful.\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), nfds);}/*处理客户端数据*/if(i > 0 && fds[i].revents & POLLIN){if(DataHandle(fds[i].fd) <= 0){if( getpeername(fds[i].fd, (Addr *)&addr, &addrlen) < 0)perror("getpeername");printf("[%s:%d][fd=%d] exited.\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), fds[i].fd);close(fds[i].fd);for(j=i; j<nfds-1; j++)fds[j] = fds[j+1];nfds--;i--;}}}}close(fd);return 0;
}
socket.c
#include "net.h"void Argment(int argc, char *argv[]){if(argc < 3){fprintf(stderr, "%s<addr><port>\n", argv[0]);exit(0);}
}
int CreateSocket(char *argv[]){/*创建套接字*/int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd < 0)ErrExit("socket");/*允许地址快速重用*/int flag = 1;if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag) ) )perror("setsockopt");/*设置通信结构体*/Addr_in addr;bzero(&addr, sizeof(addr) );addr.sin_family = AF_INET;addr.sin_port = htons( atoi(argv[2]) );/*绑定通信结构体*/if( bind(fd, (Addr *)&addr, sizeof(Addr_in) ) )ErrExit("bind");/*设置套接字为监听模式*/if( listen(fd, BACKLOG) )ErrExit("listen");return fd;
}
int DataHandle(int fd){char buf[BUFSIZ] = {};Addr_in peeraddr;socklen_t peerlen = sizeof(Addr_in);if( getpeername(fd, (Addr *)&peeraddr, &peerlen) )perror("getpeername");int ret = recv(fd, buf, BUFSIZ, 0);if(ret < 0)perror("recv");if(ret > 0){printf("[%s:%d]data: %s\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);}return ret;
}
net.h
#ifndef _NET_H_
#define _NET_H_#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5
#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)void Argment(int argc, char *argv[]);
int CreateSocket(char *argv[]);
int DataHandle(int fd);#endif
成功实现多路复用
epoll函数族
epoll
函数族用于高效的I/O事件管理,特别适用于高并发服务器应用
头文件: #include <sys/epoll.h>
-
epoll_create:
-
int epoll_create(int size);
-
功能:创建一个epoll实例,并返回一个文件描述符作为该epoll实例的标识。
-
参数
size
:之前用于定义事件队列的大小,但在Linux 2.6以后的版本中已被忽略。通常设置为0。 -
返回值:
- 成功:返回一个非负的文件描述符。
- 失败:返回-1,并设置
errno
。
-
-
epoll_ctl:
-
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
-
功能:向epoll实例中添加、修改或删除文件描述符及其相关事件。
-
参数:
epfd
:epoll实例的文件描述符。op
:操作类型,可以是EPOLL_CTL_ADD
(添加新的文件描述符)、EPOLL_CTL_MOD
(修改已注册的文件描述符的事件)或EPOLL_CTL_DEL
(删除一个文件描述符)。fd
:要操作的文件描述符。event
:指向epoll_event
结构的指针,用于指定事件类型和文件描述符的数据。
-
返回值:
- 成功:返回0。
- 失败:返回-1,并设置
errno
。
-
-
epoll_wait:
-
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
-
功能:阻塞等待已注册的文件描述符上的事件发生。
-
参数:
epfd
:epoll实例的文件描述符。events
:指向epoll_event
结构体数组的指针,用于存储发生的事件。maxevents
:可以接收的事件数量的最大值。timeout
:超时时间(以毫秒为单位),决定函数的阻塞行为。设置为0立即返回,设置为-1则无限期阻塞。
-
返回值:
- 成功:返回就绪事件的个数。
- 超时:返回0。
- 失败:返回-1,并设置
errno
。
-
epoll实现多路复用
sever.c
#include "net.h"
#include <sys/epoll.h>#define MAX_SOCK_FD 1024int main(int argc, char *argv[])
{int i, nfds, fd, epfd, newfd;Addr_in addr;socklen_t addrlen = sizeof(Addr_in);struct epoll_event tmp, events[MAX_SOCK_FD] = {};/*检查参数,小于3个 直接退出进程*/Argment(argc, argv);/*创建已设置监听模式的套接字*/fd = CreateSocket(argv);if( (epfd = epoll_create(1)) < 0)ErrExit("epoll_create");tmp.events = EPOLLIN;tmp.data.fd = fd;if( epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &tmp) )ErrExit("epoll_ctl");while(1) {if( (nfds = epoll_wait(epfd, events, MAX_SOCK_FD, -1) ) < 0)ErrExit("epoll_wait");printf("nfds = %d\n", nfds);for(i = 0; i < nfds; i++) {if(events[i].data.fd == fd){/*接收客户端连接,并生成新的文件描述符*/if( (newfd = accept(fd, (Addr *)&addr, &addrlen) ) < 0)perror("accept");printf("[%s:%d] connection.\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );tmp.events = EPOLLIN;tmp.data.fd = newfd;if( epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &tmp) )ErrExit("epoll_ctl");}else{/*处理客户端数据*/if(DataHandle(events[i].data.fd) <= 0){if( epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL) )ErrExit("epoll_ctl");if( getpeername(events[i].data.fd, (Addr *)&addr, &addrlen) )perror("getpeername");printf("[%s:%d] exited.\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );close(events[i].data.fd);}}}}close(epfd);close(fd);return 0;
}
socket.c
#include "net.h"void Argment(int argc, char *argv[]){if(argc < 3){fprintf(stderr, "%s<addr><port>\n", argv[0]);exit(0);}
}
int CreateSocket(char *argv[]){/*创建套接字*/int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd < 0)ErrExit("socket");/*允许地址快速重用*/int flag = 1;if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag) ) )perror("setsockopt");/*设置通信结构体*/Addr_in addr;bzero(&addr, sizeof(addr) );addr.sin_family = AF_INET;addr.sin_port = htons( atoi(argv[2]) );/*绑定通信结构体*/if( bind(fd, (Addr *)&addr, sizeof(Addr_in) ) )ErrExit("bind");/*设置套接字为监听模式*/if( listen(fd, BACKLOG) )ErrExit("listen");return fd;
}
int DataHandle(int fd){char buf[BUFSIZ] = {};Addr_in peeraddr;socklen_t peerlen = sizeof(Addr_in);if( getpeername(fd, (Addr *)&peeraddr, &peerlen) )perror("getpeername");int ret = recv(fd, buf, BUFSIZ, 0);if(ret < 0)perror("recv");if(ret > 0){printf("[%s:%d]data: %s\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);}return ret;
}
net.h
#ifndef _NET_H_
#define _NET_H_#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5
#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)void Argment(int argc, char *argv[]);
int CreateSocket(char *argv[]);
int DataHandle(int fd);#endif
相关文章:
【网络编程开发】11.IO模型 12.IO多路复用
11.IO模型 什么是IO: IO 是 Input/Output 的缩写,指的是输入和输出。在计算机当中,IO 操作通常指将数据从一个设备或文件中读取到计算机内存中,或将内存中的数据写入设备或文件中。这些设备可以包括硬盘驱动器、网卡、键盘、屏幕等。 通常用…...
elementui Menu 二级菜单 min-width修改无效
原因:可能是生成的二级菜单样式里面没有带特定的hash属性 而vue代码里面样式里带了 scoped生成的样式有改样式选择器 从而无法成功选择 解决:让样式可以全局选择 不带属性选择器 单文件组件 CSS 功能 | Vue.js :global(.el-menu--vertical .el-menu--p…...
字符串拼接之char实现
目录 一、前言 二、memcpy函数用法 三、代码实现 一、前言 c中想到字符串拼接,我们都知道可以用c库中std::string的字符串中的简单加法进行拼接。示例: int main() {std::string str1 "hello";std::string str2 "World";std::…...
教育的数字化转型——Kompas.ai如何变革学习体验
引言 在现代教育中,数字化转型逐渐成为提升学习效果的重要手段。随着科技的进步,人工智能(AI)在教育领域的应用越来越广泛。本文将探讨教育数字化转型的发展趋势,并介绍Kompas.ai如何通过AI技术变革学习体验。 教育数…...
域内攻击 ----> DCSync
其实严格意义上来说DCSync这个技术,并不是一种横向得技术,而是更偏向于权限维持吧! 但是其实也是可以用来横向(配合NTLM Realy),如果不牵强说得话! 那么下面,我们就来看看这个DCSyn…...
前端 JS 经典:动态执行 JS
前言:怎么将字符串当代码执行。有 4 中方式实现 eval、setTimeout、创建 script 标签、new Function 1. eval 特点:同步执行,当前作用域 var name "yq"; function exec(string) {var name "yqcoder";eval(string); …...
Laravel学习-模型注入
一. 在定义路由的时候,可以在闭包函数里引入模型, Route.get(/api/user/{user:id}, function(\App\Model\UserModel $user) {return value; }) 其中:id可以省略不写,这个意思是,请求该接口时,会UserMode…...
Django模板的使用(详细版)
1、配置 在工程中创建模板目录templates(这个名字可以变!!) 在settings.py配置文件中修改TEMPLATES配置项的DIRS值 2、定义模板 在templates目录中新建一个模板文件,如index.html 3、模板渲染 Django提供了一个函数…...
正则表达式基础
正则表达式基础 一、初识正则表达式 思考–问题答案在文末 问题1:text1 ‘height:178,weight:168,sid:123456,passwd:9527’,如何快速找出密码?问题2:text2 ‘他的电话…...
【Linux】进程间通信之命名管道
👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:Linux 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵,希望大佬指点一二 如果文章对…...
【内存管理】页表映射
页表的一些术语 现在Linux内核中支持四级页表的映射,我们先看下内核中关于页表的一些术语: 全局目录项,PGD(Page Global Directory) 上级目录项,PUD(Page Upper Directory) 中间目…...
Cloudpods 强大的多云管理平台部署
简介 Cloudpods 是一款简单、可靠的企业IaaS资源管理软件。帮助未云化企业全面云化IDC物理资源,提升企业IT管理效率。 Cloudpods 帮助客户在一个地方管理所有云计算资源。统一管理异构IT基础设施资源,极大简化多云架构复杂度和难度,帮助企业…...
深度学习的可微渲染
深度学习的可微渲染 可微渲染(Differentiable Rendering)是深度学习领域的一个重要概念,它将传统的计算机图形学与深度学习结合起来,通过使渲染过程可微分(differentiable),以便于在深度学习模…...
CAS Server Restful接口实现后台认证
背景 对于一些比较复杂定制化登录页的情况下,之前提到过可以自定义修改使用CAS Server提供的登录页这种操作已经明显跟不上复杂定制场景了,所以CAS Server也提供了支持Restful接口,支持服务端后台登陆,对于复杂登陆场景时&#x…...
Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息
0 前言 在开展系统安全检查的过程中,除了收集cpu信息,我们还需要收集内存信息。在Linux中,获取内存信息的命令很多,这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…...
【InternLM实战营第二期笔记】07:OpenCompass :是骡子是马,拉出来溜溜
文章目录 课程实操 课程 评测的意义是什么呢?我最近也在想。看到这节开头的内容后忽然有个顿悟:如果大模型最终也会变成一种基础工具(类比软件),稳定或可预期的效果需要先于用户感知构建出来,评测 case 就…...
matlab演示银河系转动动画
代码 function GalaxyRotationSimulation()% 参数设置num_stars 1000; % 恒星数量galaxy_radius 1; % 银河系半径rotation_speed 0.05; % 旋转速度% 生成银河系中的恒星分布theta 2 * pi * rand(num_stars, 1); % 角度r galaxy_radius * sqrt(rand(num_stars, 1)); % 半径…...
备战 清华大学 上机编程考试-冲刺前50%,倒数第5天
T1:多项式求和 小K最近刚刚习得了一种非常酷炫的多项式求和技巧,可以对某几类特殊的多项式进行运算。非常不幸的是,小K发现老师在布置作业时抄错了数据,导致一道题并不能用刚学的方法来解,于是希望你能帮忙写一个程序…...
leetCode127. 单词接龙
leetCode127. 单词接龙 // bfs 剪枝 class Solution { public:int ladderLength(string beginWord, string endWord, vector<string>& wordList) {// 1.将所有的单词放在set字段中unordered_set<string> s;for (auto & ele : wordList) s.insert(ele);//…...
进程概念(二)
目录 进程优先级基本概念查看系统进程PRI and NIPRI vs NI修改进程优先级的命令renice修改优先级进程其他概念 环境变量基本概念查看环境变量方法常见环境变量PATHHOMESHELL 查看环境变量环境变量相关的命令 环境变量特征命令行参数main函数中的俩个参数 argc argvmain函数的第…...
java程序100道01—20
1.用循环的嵌套,输出输出如下图形 * * * * * * * * * * * * * * * * * * * * * * * * * package Exercises.One_Hundred; public class Demo01 {public static void main(String[] args) {for(int i1;i<5;i){for(int j1;j<2*i-1;j){Sys…...
让GNSSRTK不再难【第二天-第7部分2】
状态更新计算过程: 计算卡尔曼增益: 根据预测的误差协方差矩阵 P k − P_k^- Pk− 和观测噪声协方差矩阵 R R R 计算卡尔曼增益 K k K_k Kk: K k P k − H T ( H P k − H T R ) − 1 K_k P_k^- H^T (H P_k^- H^T R)^{-1} KkPk…...
计算引擎:Flink核心概念
Apache Flink 是一个流处理框架,擅长处理实时数据流和批处理任务。Flink 提供了强大的功能来处理和分析大量数据。以下是 Flink 的核心概念: 1. DataStream 和 DataSet API DataStream API: 用于处理无界数据流,即不断生成和流动的数据。例如,传感器数据、日志等。DataSet…...
技术前沿 |【大模型InstructBLIP进行指令微调】
大模型InstructBLIP进行指令微调 一、引言二、InstructBLIP模型介绍三、指令微调训练通用视觉语言模型的应用潜力四、InstructBLIP的指令微调训练步骤五、实验结果与讨论六、结论与展望 一、引言 随着人工智能技术的快速发展,视觉语言模型(Vision-Langu…...
CSS-布局-flex
CSS3 新增了弹性盒子模型( Flexible Box 或 FlexBox ),是一种新的用于在 HTML 页面实现布局的方式。使得 HTML 页面适应不同尺寸的屏幕和不同的设备时,元素是可预测地运行。 基本概念 容器:使用 display:flex 或 display:inline-flex 声明的…...
「C系列」C 数组
文章目录 一、C 数组1. 声明数组2. 初始化数组3. 访问数组元素4. 数组越界5. 多维数组 二、C 操作数组的方法有哪些三、C 数组-应用场景1. 存储固定数量的数据2. 实现算法(如排序)3. 处理数据集合 四、相关链接 一、C 数组 在C语言中,数组是…...
Python框架scrapy有什么天赋异禀
Scrapy框架与一般的爬虫代码之间有几个显著的区别,这些差异主要体现在设计模式、代码结构、执行效率以及可扩展性等方面。下面是一些关键的不同点: 结构化与模块化: Scrapy:提供了高度结构化的框架,包括定义好的Spider…...
【ROS2大白话】四、ROS2非常简单的传参方式
系列文章目录 【ROS2大白话】一、ROS2 humble及cartorgrapher安装 【ROS2大白话】二、turtlebot3安装 【ROS2大白话】三、给turtlebot3安装realsense深度相机 【ROS2大白话】四、ROS2非常简单的传参方式 文章目录 系列文章目录前言一、launch文件传参的demo1. 编写launch.py文…...
浅谈mysql 的批量delete 和 使用in条件批量删除问题
在考虑这两个DELETE语句的性能时,我们需要考虑数据库如何执行这些查询以及它们背后可能涉及的索引和数据结构。 1.执行多个单独的DELETE语句: DELETE FROM a WHERE b 1 AND c 1; ... DELETE FROM a WHERE b 1000 AND c 1000; 这种方法的优点是每…...
【Spring Boot】过滤敏感词的两种实现
文章目录 项目场景前置知识前缀树 实现方式解决方案一:读取敏感词文件生成前缀树构建敏感词过滤器1. 导入敏感词文件 src/main/resources/sensitive_words.txt2. 构建敏感词过滤器 SensitiveFilter3. 测试与使用 解决方案二:使用第三方插件 houbb/sensit…...
电子商务网站建设开发文档/深圳关键词优化怎么样
本博客记录工程上遇到的常用的却陌生的np用法 1.np.split(分割数组) 1.1 等分数组 In [2]: x np.arange(9)In [3]: x Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8])In [8]: np.split(x,3) Out[8]: [array([0, 1, 2]), array([3, 4, 5]), array([6, 7,…...
深圳好的网站建设公司/网络外包
ospf全称:OSPF ( Open Shortest Path First )开放式最短路径优先OSPF 知识点:OSPF 基本配置,OSPF 5 种报文 ,7种邻居状态,4 种网络类型,4 种特殊区域,7类LSA,4 种 link type…...
广水做网站/中国2022年重大新闻
要带本科毕设嘛,所以对这个要多少了解一下,然后就搜索了一下,之前搜索过一次,没发现什么啊,这次一搜索发现不错的东西,特此记录,主要是转载。以下内容转载自CVPR 2019轨迹预测竞赛冠军方法总结赛…...
学校网站免费建设/经典广告
在一些批处理或者系统技巧操作教程文章中,我们常常会看到一些形如 %windir% 或者 %systemdrive% 的变量。这些变量都代表着什么含义呢?下面小技巧之家为大家整理了在Windows XP下系统变量方式表达相对应的路径,大家可以看得更加清楚明白了&am…...
银川网站建设报价/推广方案100个
一、向量:1.直接相等加减直接D3DXVec3LengthD3DXVec3Normalize数乘直接2.点乘D3DXVec3Dot点乘D3DXVec3Dot,主要计算夹角(投影角)V1( x1, y1).V2(x2, y2) x1*x2 y1*y2A.B |A||B|Cos(θ)FLOAT D3DXVec3Dot(_In_ const D3DXVECTOR3 *pV1,_In…...
青岛网站建设推广/新疆今日头条新闻
南京整站网站优化哪个公司好热电咨询,SEO推广并不像看上去那么简单,这不仅包含了SEO的关键技术,也是优化思维中重要的部分。只有在思维上敢于突破,不断创新,还要掌握SEO优化技术,在推广策略上敢于做“吃螃蟹的人”&…...