I/O多路复用三种实现

 

一.select 实现

(1)select流程

基本流程是:

1. 先构造一张有关文件描述符的表;                     fd_set readfds

2. 清空表                                                              FD_ZERO()

3. 将你关心的文件描述符加入到这个表中;           FD_SET()

4. 调用select函数。                                              selset()

5. 判断是哪一个或哪些文件描述符产生了事件(IO操作);   FD_ISSET()

6. 做对应的逻辑处理;       

(2)selset函数

头文件: #include<sys/select.h>   #include<sys/time.h>   

             #include<sys/types.h>   #include<unistd.h>

声明: int select(int nfds, fd_set *readfds, fd_set *writefds,\

                                    fd_set *exceptfds, struct timeval *timeout);

功能:监测是哪些文件描述符产生事件,阻塞等待产生.

参数:nfds:    监测的最大文件描述个数(文件描述符从0开始,这里是个数,记得+1)

          readfds:  读事件集合; // 键盘鼠标的输入,客户端连接都是读事件

          writefds: 写事件集合;  //NULL表示不关心

          exceptfds:异常事件集合;  //NULL 表示不关心

          timeout:   设为NULL,等待直到某个文件描述符发生变化;

                              设为大于0的值,有描述符变化或超时时间到才返回。

        超时时间检测:如果规定时间内未完成函数功能,返回一个超时的信息,我们可以根据该信息设定相应需求;

返回值:  <0 出错            >0 表示有事件产生;

                如果设置了超时检测时间:&tv

                <0 出错            >0 表示有事件产生;      ==0 表示超时时间已到;        

结构体如下:                     

            struct timeval {

               long    tv_sec;         以秒为单位,指定等待时间

               long    tv_usec;        以毫秒为单位,指定等待时间

           };

void FD_CLR(int fd, fd_set *set);  //将set集合中的fd清除掉 

int  FD_ISSET(int fd, fd_set *set); //判断fd是否在set集合中产生了事件

void FD_SET(int fd, fd_set *set);  //将fd加入到集合中

void FD_ZERO(fd_set *set);          //清空集合

(3)Select特点:

Select特点:

1. 一个进程最多只能监听1024个文件描述符 (32位)   [64位为 2048]

2. select被唤醒之后要重新轮询(0-1023)一遍驱动,效率低(消耗CPU资源)

3. select每次会清空未响应的文件描述符,每次都需要拷贝用户空间的表到内核空间,效率低,开销较大

   (0~3G是用户态,3G~4G是内核态,两个状态来回切换  拷贝是非常耗时,耗资源的)

 (4)select机制: 

1. 头文件检测1024个文件描述符  0-1023

2. 在select中0~2存储标准输入、标准输出、标准出错    

3. 监测的最大文件描述个数为fd+1(如果fd = 3,则最大为 4) :  //因为从0开始的    

4. select只对置1的文件描述符感兴趣 ,假如事件产生,select检测时 , 产生的文件描述符会保持1,未产生事件的会置0; 

5. select每次轮询都会清空表(置零的清空)   //需要在select前备份临时表

练习1:

如何通过select实现 响应鼠标事件同时响应键盘事件?

代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>int main(int argc, char const *argv[])
{int fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open is err:");return -1;}//1.创建表fd_set readfds;//2/清空表FD_ZERO(&readfds);//3.设置表FD_SET(0, &readfds);FD_SET(fd, &readfds);fd_set readfdcp = readfds;int maxfd = fd;char buf[128] = {0};while (1){//4.检测是否有相应select(maxfd + 1, &readfds, NULL, NULL, NULL);//5.检测哪一个文件描述符if (FD_ISSET(0, &readfds)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("key: %s\n", buf);}if (FD_ISSET(fd, &readfds)){int ret = read(fd, buf, sizeof(buf));buf[ret] = '\0';printf("mouse: %s\n", buf);}readfds = readfdcp;}return 0;
}

 练习2:

select是文件描述符和下标一一对应,0只能对应0号文件描述符。因此只有最大的文件描述符关闭时,才--len。注意增加删除时是针对实际表,不是临时表。

使用select实现server的全双工

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建表fd_set readfds;//2/清空表FD_ZERO(&readfds);//3.设置表FD_SET(0, &readfds);FD_SET(sockfp, &readfds);fd_set readfdcp = readfds;int maxfd = sockfp;struct timeval st;while (1){readfds = readfdcp;//4.检测是否有响应st.tv_sec = 5;st.tv_usec = 0;int ret = select(maxfd + 1, &readfds, NULL, NULL, &st);if (ret < 0){perror("select err");return -1;}else if (ret == 0){printf("无响应\n");}//0响应,证明服务器要发送消息if (FD_ISSET(0, &readfds)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';for (int i = 4; i <= maxfd; ++i){send(i, buf, sizeof(buf), 0);}}//sockfp,监听套接字响应证明,有客户端要链接if (FD_ISSET(sockfp, &readfds)){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));FD_SET(acceptfp, &readfdcp);if (acceptfp > maxfd)maxfd = acceptfp;}//检测客户端,检查是哪一个客户端发送的消息for (int i = 4; i <= maxfd; ++i){if (FD_ISSET(i, &readfds)){int recvbyte = recv(i, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", i);close(i);FD_CLR(i, &readfdcp);if (i == maxfd)--maxfd;}else{printf("%d : %s\n", i, buf);}}}}return 0;
}

练习3:

使用select实现client的全双工

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>int main(int argc, const char *argv[])
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd < 0){perror("socker is err:");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr(argv[2]);if(connect(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0){perror("connect is err:");return -1;}//1.创建表fd_set readfds,tempfds;//2.清空表FD_ZERO(&readfds);FD_ZERO(&tempfds);//3.添加文件描述符FD_SET(0,&readfds);FD_SET(sockfd,&readfds);int maxfd = sockfd;int ret;char buf[128];while(1){tempfds = readfds;//4.调select检测ret = select(maxfd+1,&tempfds,NULL,NULL,NULL);if(ret < 0){perror("select is err:");return -1;}if(FD_ISSET(0,&tempfds)){fgets(buf,sizeof(buf),stdin);if(buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';send(sockfd,buf,sizeof(buf),0);}if(FD_ISSET(sockfd,&tempfds)){int recvbyte = recv(sockfd,buf,sizeof(buf),0);if(recvbyte < 0){perror("recv is err:");return -1;}printf("%s\n",buf);}}close(sockfd);return 0;
}

(5)select的超时时间检测:

超时检测的必要性:

1. 避免进程在没有数据时无限制的阻塞;

2. 规定时间未完成语句应有的功能,则会执行相关功能;

结构体如下:                     

            struct timeval {

               long    tv_sec;         以秒为单位,指定等待时间

               long    tv_usec;        以毫秒为单位,指定等待时间

           };

二.poll实现

 (1)poll流程

使用:  1.先创建结构体数组                                           struct pollfd fds[100];

          2.添加结构体成员的文件描述符以及触发方式   fds[0].fd = ?;fds[0].events = POLLIN 

          3.保存数组内最后一个有效元素的下标       

          4. 调用函数poll                                                  ret = poll(fds,nfds+1,-1);

          5.判断结构体内文件描述符是否触发事件          fds[i].revents == POLLIN

          6.根据不同的文件描述符触发不同事件 

(2)poll函数

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

头文件: #include<poll.h>

功能: 监视并等待多个文件描述符的属性变化

参数:

  1.struct pollfd *fds:   关心的文件描述符数组,大小自己定义

   若想检测的文件描述符较多,则建 立结构体数组struct pollfd fds[N]; 

           struct pollfd

           {

                  int fd;        //文件描述符

             short events;  //等待的事件触发条件----POLLIN读时间触发(大多数)

             short revents; //实际发生的事件(未产生事件: 0 ))

            }

    2.   nfds:        最大文件描述符个数

    3.  timeout: 超时检测 (毫秒级):1000 == 1s      

                          如果-1,阻塞          如果0,不阻塞

返回值:  <0 出错              >0 表示有事件产生;

              如果设置了超时检测时间:&tv

                <0 出错                >0 表示有事件产生;            ==0 表示超时时间已到;

(3)poll特点

1. 优化文件描述符个数的限制;

(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组容量为1,如果想监听100个,那么这个结构体数组的容量就为100,多少文件描述符由程序员自己来决定)

2. poll被唤醒之后需要重新轮询一遍驱动,效率比较低(消耗CPU)

3. poll不需重新构造文件描述符表(也不需清空表),只需要从用户空间向内核空间拷贝一次数据(效率相对比较高)

练习: 

使用poll实现server的全双工

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建结构体数组struct pollfd fds[100];//2.添加文件描述符和触发方式fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = sockfp;fds[1].events = POLLIN;int nfds = 1;int ret;while (1){//3.poll轮循检测ret = poll(fds, nfds + 1, 2000);if (ret < 0){perror("poll is err");return -1;}else if (ret == 0){printf("qeqweqe\n");continue;}//4. 判断哪一个文件描述符产生响应,并发布任务for (int i = 0; i <= nfds; ++i){if (fds[i].revents == POLLIN){if (fds[i].fd == 0){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';//printf("发送信息:\n");for (int j = 2; j <= nfds; ++j){send(fds[j].fd, buf, sizeof(buf), 0);}}else if (fds[i].fd == sockfp){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));fds[++nfds].fd = acceptfp;fds[nfds].events = POLLIN;}else{int recvbyte = recv(fds[i].fd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", i);close(fds[i].fd);//覆盖fds[i] = fds[nfds];//--i,--nfds后,最后一个循环不到--nfds, --i;}else{printf("%d : %s\n", i, buf);}}}}}return 0;
}

(4)poll超时时间检测

 timeout: 超时检测 (毫秒级):1000 == 1s      

                  如果-1,阻塞          如果0,不阻塞

三.epoll实现

(1)epoll流程:

Epoll的使用:

1.创建红黑树 和 就绪链表                                      int epfd = epoll_create(1);

2.添加文件描述符和事件信息到树上

    event.events = EPOLLIN|EPOLLET;

    event.data.fd = 0;

    epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event

3.阻塞等待事件的产生,一旦产生事件,则进行处理 

     int ret = epoll_wait(epfd,events,32,-1);

4.根据链中准备处理的文件描述符 进行处理

(2)epoll函数族 

epoll 要使用一组函数:       epoll_create 创建红黑树 和 就序链表

                                          epoll_ctl   添加文件描述符和事件到树上 / 从树上删除

                                          epoll_wait  等待事件产生

epoll_create 

创建红黑树以及链表

头文件:#include <sys/epoll.h>

声明:int epoll_create(int size);

功能:创建红黑树根节点(创建epoll实例) , 同时也会创建就绪链表

返回值:成功时返回一个实例(二叉树句柄),失败时返回-1。

epoll_ctl

控制epoll属性

声明: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:控制epoll属性,比如给红黑树添加节点

参数:  1. epfd:   epoll_create函数的返回句柄。//一个标识符

          2. op:表示动作类型,有三个宏:         

                EPOLL_CTL_ADD:注册新的fd到epfd中

                EPOLL_CTL_MOD:修改已注册fd的监听事件

                EPOLL_CTL_DEL:从epfd中删除一个fd

3. 要操作的文件描述符

4. 结构体信息:

        typedef union epoll_data {

                int fd; //要添加的文件描述符,只用这个

                uint32_t u32; typedef unsigned int

                uint64_t u64; typedef unsigned long int

        } epoll_data_t;

        struct epoll_event {

                uint32_t events; 事件

                epoll_data_t data; //共用体(看上面)

        };

           关于events事件:

            EPOLLIN:  表示对应文件描述符可读

            EPOLLOUT: 可写

            EPOLLPRI:有紧急数据可读;

            EPOLLERR:错误;

            EPOLLHUP:被挂断;

            EPOLLET:触发方式,边缘触发;(默认使用边缘触发)

            ET模式:表示状态的变化;

            NULL: 删除一个文件描述符使用,无事件

返回值:成功:0, 失败:-1

epoll_wait

等待事件产生

声明: int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

功能:等待事件产生

   内核会查找红黑树中有事件响应的文件描述符, 并将这些文件描述符放入就绪链表

    就绪链表中的内容, 执行epoll_wait会同时复制到第二个参数events

参数:   epfd:句柄;

           events:用来保存从就绪链表中响应事件的集合;(传出参数,定义结构体数组)

           maxevents:  表示每次在链表中拿取响应事件的个数;

           timeout:超时时间,毫秒,0立即返回  ,-1阻塞

返回值: 成功: 实际从链表中拿出的数目     失败时返回-1

(4)epoll特点

1.监听的最大的文件描述符没有个数限制(取决与你自己的系统 1GB - 10万个左右)

2.异步I/O,epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高

3.epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.

(5)epoll机制

select,poll都属于 同步IO机制(轮询)

epoll属于异步IO机制(不轮询): 

Epoll处理高并发,百万级

  1. 红黑树: 是特殊的二叉树(每个节点带有属性),Epoll怎样能监听很多个呢?首先创建树的根节点,每个节点都是一个fd以结构体的形式存储(节点里面包含了一些属性,callback函数)
  2. 就绪链表: 当某一个文件描述符产生事件后,会自动调用callback函数,通过回调callback函数来找到链表对应的事件(读时间还是写事件)。

 

 练习:

epoll实现server

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建红黑树以及链表//树的跟节点/树的句柄int epfd = epoll_create(1);//2.上树struct epoll_event event;struct epoll_event events[32] ;event.events = EPOLLET | EPOLLIN;event.data.fd = 0;epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);event.data.fd = sockfp;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfp, &event);while (1){//3.阻塞等待文件描述符产生事件int ret = epoll_wait(epfd, events, 32, -1);printf("asdsdfgdsf\n");if (ret < 0){perror("epoll err");return -1;}//4.根据文件描述符号,进行处理for (int i = 0; i < ret; ++i){if (events[i].data.fd == 0){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("发送信息:\n");//send(fds[j].fd, buf, sizeof(buf), 0);}else if (events[i].data.fd == sockfp){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));//上树event.data.fd = acceptfp;epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfp, &event);}else{int recvbyte = recv(events[i].data.fd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", events[i].data.fd);close(events[i].data.fd);//下树epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);}else{printf("%d : %s\n", events[i].data.fd, buf);}}}}return 0;
}

对比 

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

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

相关文章

数据分享|R语言逻辑回归、线性判别分析LDA、GAM、MARS、KNN、QDA、决策树、随机森林、SVM分类葡萄酒交叉验证ROC...

全文链接:http://tecdat.cn/?p27384 在本文中&#xff0c;数据包含有关葡萄牙“Vinho Verde”葡萄酒的信息&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 介绍 该数据集&#xff08;查看文末了解数据获取方式&#xff09;有1599个观测值和12个变量&#xf…

ChatGPT的未来

随着人工智能的快速发展&#xff0c;ChatGPT作为一种自然语言生成模型&#xff0c;在各个领域都展现出了巨大的潜力。它不仅可以用于日常对话、创意助手和知识查询&#xff0c;还可以应用于教育、医疗、商业等各个领域&#xff0c;为人们带来更多便利和创新。 在教育领域&#…

安装深度(Deepin)系统

Deepin系统安装 Deepin是和Ubuntu一样&#xff0c;是一个基于Debian的Linux的发型版本。 Deepin相对于Ubuntu&#xff0c;Deepin更适合中国用户的使用习惯。 一 官网工具制作启动盘 制作启动盘、和安装系统&#xff0c;操作非常简单&#xff0c;nice&#xff01; 官网提供了…

【云计算】虚拟私有网络 VPC

虚拟私有网络 VPC 1.VPC 简介1.1 VPC 相关基本概念1.2 其他相关基本概念 2.VPC 通信场景2.1 VPC 内部互通2.2 VPC 间互通2.2.1 对等连接2.2.2 Transit Gateway 或者云联网 2.3 访问 Internet2.3.1 Internet 网关2.3.2 NAT 网关 2.4 访问本地网络2.4.1 VPN 连接2.4.2 专线接入2.…

uniapp微信小程序地图实现周边

官方说明&#xff1a;小程序JavascriptSDK使用指南 - 微信小程序解决方案 | 腾讯位置服务https://lbs.qq.com/product/miniapp/jssdk/ 先申请腾讯地图的开发者密钥&#xff0c;申请地址&#xff1a;腾讯位置服务 - 立足生态&#xff0c;连接未来 申请密钥时&#xff0c;需要勾…

基于matlab实现的弹簧振动系统模型程序(动态模型)

完整代码&#xff1a; clear all; %System data m1.0; zeta0.01; omega01.0; Dt1.0; f01.0; x00.0; dotx00.0; xmaxsqrt(x0^2(dotx0/omega0)^2)min([0.5*abs(f0)*Dt/(m*omega0) f0/omega0^2]); omegadomega0*sqrt(1-zeta^2); dt00.1*pi/omega0; nstep500; a0.70; b0.…

postgresql-视图

postgresql-视图 视图概述使用视图的好处 创建视图修改视图删除视图递归视图可更新视图WITH CHECK OPTION 视图概述 视图&#xff08;View&#xff09;本质上是一个存储在数据库中的查询语句。视图本身不包含数据&#xff0c;也被称为 虚拟表。我们在创建视图时给它指定了一个…

腾讯云镜像TencentOS Server操作系统介绍、性能稳定性测评

腾讯云TencentOS Server镜像是腾讯云推出的Linux操作系统&#xff0c;完全兼容CentOS生态和操作方式&#xff0c;TencentOS Server操作系统为云上运行的应用程序提供稳定、安全和高性能的执行环境&#xff0c;TencentOS可以运行在腾讯云CVM全规格实例上&#xff0c;包括黑石物理…

天翎知识管理系统:智能化搜索引擎,快速定位知识资源

关键词&#xff1a;知识管理系统、全文检索 编者按&#xff1a;在当今知识经济时代&#xff0c;企业所面临的知识资源越来越丰富&#xff0c;如何高效地管理和利用这些资源成为了一个重要的问题。天翎知识管理系统凭借其智能化搜索引擎&#xff0c;可以帮助企业快速定位知识资源…

Windows11系统下配置JAVA环境变量

一、环境变量的配置 1、右键开始菜单按钮&#xff0c;点击【系统➡高级系统设置】 2、在弹出的系统属性界面点击环境变量 3、在弹出的“环境变量”框&#xff0c;中选择下方的系统变量&#xff0c;点击新建 4、在弹出的“新建系统变量”框中&#xff0c;输入变量名和变量值&am…

数据预处理-分箱(Binning)和 WOE编码

数据预处理-分箱&#xff08;Binning&#xff09;和 WOE编码 1. 分箱 1.1 理论 1.1.1 定义 分箱就是将连续的特征离散化&#xff0c;以某种方式将特征值映射到几个箱(bin)中。 1.1.2 为什么要进行分箱&#xff1f; 引入非线性变换&#xff0c;增强模型性能。因为原始值和目…

【2023集创赛】加速科技杯作品:高光响应的二硫化铼光电探测器

本文为2023年第七届全国大学生集成电路创新创业大赛&#xff08;“集创赛”&#xff09;加速科技杯西北赛区二等奖作品分享&#xff0c;参加极术社区的【有奖征集】分享你的2023集创赛作品&#xff0c;秀出作品风采&#xff0c;分享2023集创赛作品扩大影响力&#xff0c;更有丰…