vim 打开退出保存

打开:
直接打开 vim practice_1.txt
也可以先打开vim,后:e 文件路径

保存:
:w 文件名
或者退出保存:wq!

普通模式下退出vim:Shift+zz


那我们先来总结一下select模型的缺点:

           2、在进行opencv源代码的下载过程中速度很慢,建议FQ。参考:

vim 多文件编辑

vim 1.txt 2.txt
进入vim后打开新文件

命令行模式下输入:e 3.txt 打开新文件3.txt
命令行模式下输入:e# 回到前一个文件
命令行模式下输入:ls可以列出以前编辑过的文档
命令行模式下输入:b 2.txt(或者编号)可以直接进入文件2.txt编辑
命令行模式下输入:bd
2.txt(或者编号)可以删除以前编辑过的列表中的文件项目
命令行模式下输入:e! 4.txt,新打开文件4.txt,放弃正在编辑的文件
命令行模式下输入:f 显示正在编辑的文件名
命令行模式下输入:f new.txt,改变正在编辑的文件名字为new.txt

如果因为断电等原因造成文档没有保存,可以采用恢复方式,vim
-r进入文档后,输入:ewcover 1.txt来恢复
$ vim -r 1.txt


 

epoll的一个简单使用范例

#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>



#define MAXLINE 5
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#define INFTIM 1000

void setnonblocking(int sock)
{
    int opts;
    opts=fcntl(sock,F_GETFL);
    if(opts<0)
    {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }
    opts = opts|O_NONBLOCK;
    if(fcntl(sock,F_SETFL,opts)<0)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
}

int main(int argc, char* argv[])
{
    int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber;
    ssize_t n;
    char line[MAXLINE];
    socklen_t clilen;


    if ( 2 == argc )
    {
        if( (portnumber = atoi(argv[1])) < 0 )
        {
            fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]);
            return 1;
        }
    }
    else
    {
        fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]);
        return 1;
    }



    //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件

    struct epoll_event ev,events[20];
    //生成用于处理accept的epoll专用的文件描述符

    epfd=epoll_create(256);
    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    //把socket设置为非阻塞方式

    //setnonblocking(listenfd);

    //设置与要处理的事件相关的文件描述符

    ev.data.fd=listenfd;
    //设置要处理的事件类型

    ev.events=EPOLLIN|EPOLLET;
    //ev.events=EPOLLIN;

    //注册epoll事件

    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    char *local_addr="127.0.0.1";
    inet_aton(local_addr,&(serveraddr.sin_addr));//htons(portnumber);

    serveraddr.sin_port=htons(portnumber);
    bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr));
    listen(listenfd, LISTENQ);
    maxi = 0;
    for ( ; ; ) {
        //等待epoll事件的发生

        nfds=epoll_wait(epfd,events,20,500);
        //处理所发生的所有事件

        for(i=0;i<nfds;++i)
        {
            if(events[i].data.fd==listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。

            {
                connfd = accept(listenfd,(struct sockaddr *)&clientaddr, &clilen);
                if(connfd<0){
                    perror("connfd<0");
                    exit(1);
                }
                //setnonblocking(connfd);

                char *str = inet_ntoa(clientaddr.sin_addr);
                printf("accapt a connection from\n ");
                //设置用于读操作的文件描述符

                ev.data.fd=connfd;
                //设置用于注测的读操作事件

                ev.events=EPOLLIN|EPOLLET;
                //ev.events=EPOLLIN;

                //注册ev

                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
            }
            else if(events[i].events&EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。

            {
                printf("EPOLLIN\n");
                if ( (sockfd = events[i].data.fd) < 0)
                    continue;
                if ( (n = read(sockfd, line, MAXLINE)) < 0) {
                    if (errno == ECONNRESET) {
                        close(sockfd);
                        events[i].data.fd = -1;
                    } else
                        printf("readline error\n");
                } else if (n == 0) {
                    close(sockfd);
                    events[i].data.fd = -1;
                }
                if(n<MAXLINE-2)
                    line[n] = '\0';

                //设置用于写操作的文件描述符

                ev.data.fd=sockfd;
                //设置用于注测的写操作事件

                ev.events=EPOLLOUT|EPOLLET;
                //修改sockfd上要处理的事件为EPOLLOUT

                //epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

            }
            else if(events[i].events&EPOLLOUT) // 如果有数据发送

            {
                sockfd = events[i].data.fd;
                write(sockfd, line, n);
                //设置用于读操作的文件描述符

                ev.data.fd=sockfd;
                //设置用于注测的读操作事件

                ev.events=EPOLLIN|EPOLLET;
                //修改sockfd上要处理的事件为EPOLIN

                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
            }
        }
    }
    return 0;
}

图片 1

常用vim操作自我总结,大神请回避:

针对select模型的缺点,epoll模型被提出来了!

 

vim 移动和进入插入模式

移动:
w下一个单词
b上一个单词

进入插入模式
命令 说明
i 在当前光标处进行编辑
I 在行首插入
A 在行末插入
a 在光标后插入编辑
o 在当前行后插入一个新行
O 在当前行前插入一个新行
cw 替换从光标所在位置后到一个单词结尾的字符


 

  1. 单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;(在linux内核头文件中,有这样的定义:#define
    __FD_SETSIZE 1024)
  2. 内核 /
    用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销;
    select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;
  3. select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程。

    OpenCV的全称是:Open Source
Computer Vision Library。OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac
OS操作系统上。它轻量级而且高效——由一系列C函数和少量C++ 类构成,同时提供了Python、Ruby、Matlab等语言的接口实现了图像处理和计算机视觉方面的很多通用算法。

Vim重复命令

.
也可以输入数字在命令前


 

带ET和LT双模式的epoll服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <errno.h>
#include <stdbool.h>


#define MAX_EVENT_NUMBER 1024  //event的最大数量
#define BUFFER_SIZE 10      //缓冲区大小
#define ENABLE_ET  1       //是否启用ET模式

/* 将文件描述符设置为非拥塞的  */
int SetNonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

/* 将文件描述符fd上的EPOLLIN注册到epoll_fd指示的epoll内核事件表中,参数enable_et指定是否对fd启用et模式 */
void AddFd(int epoll_fd, int fd, bool enable_et)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN; //注册该fd是可读的
    if(enable_et)
    {
        event.events |= EPOLLET;
    }

    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);  //向epoll内核事件表注册该fd
    SetNonblocking(fd);
}

/*  LT工作模式特点:稳健但效率低 */
void lt_process(struct epoll_event* events, int number, int epoll_fd, int listen_fd)
{
    char buf[BUFFER_SIZE];
    int i;
    for(i = 0; i < number; i++) //number: 就绪的事件数目
    {
        int sockfd = events[i].data.fd;
        if(sockfd == listen_fd)  //如果是listen的文件描述符,表明有新的客户连接到来
        {
            struct sockaddr_in client_address;
            socklen_t client_addrlength = sizeof(client_address);
            int connfd = accept(listen_fd, (struct sockaddr*)&client_address, &client_addrlength);
            AddFd(epoll_fd, connfd, false);  //将新的客户连接fd注册到epoll事件表,使用lt模式
        }
        else if(events[i].events & EPOLLIN) //有客户端数据可读
        {
            // 只要缓冲区的数据还没读完,这段代码就会被触发。这就是LT模式的特点:反复通知,直至处理完成
            printf("lt mode: event trigger once!\n");
            memset(buf, 0, BUFFER_SIZE);
            int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
            if(ret <= 0)  //读完数据了,记得关闭fd
            {
                close(sockfd);
                continue;
            }
            printf("get %d bytes of content: %s\n", ret, buf);

        }
        else
        {
            printf("something unexpected happened!\n");
        }
    }
}

/* ET工作模式特点:高效但潜在危险 */
void et_process(struct epoll_event* events, int number, int epoll_fd, int listen_fd)
{
    char buf[BUFFER_SIZE];
    int i;
    for(i = 0; i < number; i++)
    {
        int sockfd = events[i].data.fd;
        if(sockfd == listen_fd)
        {
            struct sockaddr_in client_address;
            socklen_t client_addrlength = sizeof(client_address);
            int connfd = accept(listen_fd, (struct sockaddr*)&client_address, &client_addrlength);
            AddFd(epoll_fd, connfd, true);  //使用et模式
        }
        else if(events[i].events & EPOLLIN)
        {
            /* 这段代码不会被重复触发,所以我么循环读取数据,以确保把socket读缓存的所有数据读出。这就是我们消除ET模式潜在危险的手段 */

            printf("et mode: event trigger once!\n");
            while(1)
            {
                memset(buf, 0, BUFFER_SIZE);
                int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
                if(ret < 0)
                {
                    /* 对于非拥塞的IO,下面的条件成立表示数据已经全部读取完毕,此后epoll就能再次触发sockfd上的EPOLLIN事件,以驱动下一次读操作 */

                    if(errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        printf("read later!\n");
                        break;
                    }

                    close(sockfd);
                    break;
                }
                else if(ret == 0)
                {
                    close(sockfd);
                }
                else //没读完,继续循环读取
                {
                    printf("get %d bytes of content: %s\n", ret, buf);
                }
            }
        }
        else
        {
            printf("something unexpected happened!\n");
        }
    }
}


int main(int argc, char* argv[])
{
    if(argc <= 2)
    {
        printf("usage:  ip_address + port_number\n");
        return -1;
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret = -1;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listen_fd = socket(PF_INET, SOCK_STREAM, 0);
    if(listen_fd < 0)
    {
        printf("fail to create socket!\n");
        return -1;
    }

    ret = bind(listen_fd, (struct sockaddr*)&address, sizeof(address));
    if(ret == -1)
    {
        printf("fail to bind socket!\n");
        return -1;
    }

    ret = listen(listen_fd, 5);
    if(ret == -1)
    {
        printf("fail to listen socket!\n");
        return -1;
    }

    struct epoll_event events[MAX_EVENT_NUMBER];
    int epoll_fd = epoll_create(5);  //事件表大小为5
    if(epoll_fd == -1)
    {
        printf("fail to create epoll!\n");
        return -1;
    }

    AddFd(epoll_fd, listen_fd, true); //使用ET模式epoll,将listen文件描述符加入事件表

    while(1)
    {
        int ret = epoll_wait(epoll_fd, events, MAX_EVENT_NUMBER, -1);
        if(ret < 0)
        {
            printf("epoll failure!\n");
            break;
        }

        if(ENABLE_ET)
        {
            et_process(events, ret, epoll_fd, listen_fd);
        }
        else
        {
            lt_process(events, ret, epoll_fd, listen_fd);  
        }

    }

    close(listen_fd);
    return 0;

}

然后再写一个简单的TCP客户端来测试一下:

//客户端
#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <sys/time.h>

int main() 
{ 
    int client_sockfd; 
    int len; 
    struct sockaddr_in address;//服务器端网络地址结构体 
     int result; 
    char str1[] = "ABCDE"; 
    char str2[] = "ABCDEFGHIJK"; 
    client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立客户端socket 
    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = htons(8888); 
    len = sizeof(address); 
    result = connect(client_sockfd, (struct sockaddr *)&address, len); 
    if(result == -1) 
    { 
         perror("oops: client2"); 
         exit(1); 
    } 
    //第一次读写
    write(client_sockfd, str1, sizeof(str1)); 

    sleep(5);

    //第二次读写
    write(client_sockfd, str2, sizeof(str2)); 


    close(client_sockfd); 

    return 0; 
}

TCP客户端的动作是这样的:第一次先发送字符串”ABCDE”过去服务器端,5秒后,再发字符串”ABCDEFGHIJK”过去服务端,我们观察一下ET模式的服务器和LT模式的服务器在读取数据的方式上到底有什么区别。

ET模式

图片 2

ET模式现象分析:我们的服务器读缓冲区大小我们设置了10。第一次接受字符串时,我们的缓冲区有足够的空间接受它,所以打印出内容”ABCDE”并且打印出”read
later”表示数据已经读完了。第二次接收字符串时,我们的缓冲区空间不足以接收所有的字符,所以分了两次接收。但是总触发次数仅为2次。

LT模式

图片 3

LT模式现象分析:
同理,第一次接受字符串有足够的空间接受,第二次接收字符串缓冲区空间不足,所以第二次接收时分了两次来接受。同时也注意到,只要你没有完全接收完上次的数据,内核就会继续通知你去接收数据!所以事件触发的次数是3次。

然后运行以下命令:

vim行间跳转和行内跳转

nG 跳到第n行
gg 第一行
G最后一行
ctrol + o 跳到上一次跳转时的位置,可以重复类似history

命令 说明
w 到下一个单词的开头
e 到下一个单词的结尾
b 到前一个单词的开头
ge 到前一个单词的结尾
0或^ 到行头
$ 到行尾
f<字母> 向后搜索<字母>并跳转到第一个匹配的位置(非常实用)
F<字母> 向前搜索<字母>并跳转到第一个匹配的位置
t<字母>
向后搜索<字母>并跳转到第一个匹配位置之前的一个字母(不常用)
T<字母>
向前搜索<字母>并跳转到第一个匹配位置之后的一个字母(不常用)


 

EPOLLONESHOT事件

即使我们使用ET模式,一个socket上的某个事件还是可能被触发多次,这在并发程序中就会引发一些问题。比如一个县城在读取完某个socket上的数据后开始处理这些数据,而在数据的出来过程中该socket上又有新数据可读(EPOLLIN再次被触发),此时另一个县城被唤醒来读取这些新数据。于是就出现了两个线程同时操作一个socket的局面。这当然不是我们所期望的,我们期望的是一个socket连接在任一时刻都只被一个线程处理。这一点可以使用EPOLLONESHOT事件实现。

对于注册了EPOLLONSHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写或者异常事件,且只触发一次,除非我们使用epoll_ctl函数重置该文件描述符上注册的EPOLLONESHOT事件。这样,当一个线程在处理某个socket时,其他线程是不可能有机会操作该socket的。但反过来思考,注册了EPOLLONESHOT事件的socket一旦被某个线程处理完毕,该线程就应该立即重置这个socket上的EPOLLONESHOT事件,以确保这个socket下一次可读时,其EPOLLIN事件能被触发,进而让其他工作线程有机会继续处理这个socket。

下面是一个使用了EPOLLONESHOT的epoll服务器

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <stdbool.h>

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

struct fds
{
    int epollfd;
    int sockfd;
};

int SetNonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

void AddFd(int epollfd, int fd, bool oneshot)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    if(oneshot)
    {
        event.events |= EPOLLONESHOT;
    }

    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    SetNonblocking(fd);
}

/*重置fd上的事件,这操作以后,尽管fd上的EPOLLONESHOT事件被注册,但是操作系统仍然会触发fd上的EPOLLIN事件,且只触发一次*/
void reset_oneshot(int epollfd, int fd)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

/*工作线程*/
void* worker(void* arg)
{
    int sockfd = ((struct fds*)arg)->sockfd;
    int epollfd = ((struct fds*)arg)->epollfd;
    printf("start new thread to receive data on fd: %d\n", sockfd);
    char buf[BUFFER_SIZE];
    memset(buf, 0, BUFFER_SIZE);

    while(1)
    {
        int ret = recv(sockfd, buf,BUFFER_SIZE-1, 0);
        if(ret == 0)
        {
            close(sockfd);
            printf("foreigner closed the connection\n");
            break;
        }
        else if(ret < 0)
        {
            if(errno = EAGAIN)
            {
                reset_oneshot(epollfd, sockfd);
                printf("read later\n");
                break;
            }
        }
        else
        {
            printf("get content: %s\n", buf);
            //休眠5秒,模拟数据处理过程
            printf("worker working...\n");
            sleep(5);
        }
    }
    printf("end thread receiving data on fd: %d\n", sockfd);
}

int main(int argc, char* argv[])
{
    if(argc <= 2)
    {
        printf("usage: ip_address + port_number\n");
        return -1;
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret = -1;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    if(listenfd < 0)
    {
        printf("fail to create socket!\n");
        return -1;
    }

    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
    if(ret == -1)
    {
        printf("fail to bind socket!\n");
        return -1;
    }

    ret = listen(listenfd, 5);
    if(ret == -1)
    {
        printf("fail to listen socket\n");
        return -1;
    }

    struct epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    if(epollfd == -1)
    {
        printf("fail to create epoll\n");
        return -1;
    }

    //注意,监听socket listenfd上是不能注册EPOLLONESHOT事件的,否则应用程序只能处理一个客户连接!因为后续的客户连接请求将不再触发listenfd的EPOLLIN事件
    AddFd(epollfd, listenfd, false);


    while(1)
    {
        int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);  //永久等待
        if(ret < 0)
        {
            printf("epoll failure!\n");
            break;
        }

        int i;
        for(i = 0; i < ret; i++)
        {
            int sockfd = events[i].data.fd;
            if(sockfd == listenfd)
            {
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof(client_address);
                int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
                //对每个非监听文件描述符都注册EPOLLONESHOT事件
                AddFd(epollfd, connfd, true);
            }
            else if(events[i].events & EPOLLIN)
            {
                pthread_t thread;
                struct fds fds_for_new_worker;
                fds_for_new_worker.epollfd = epollfd;
                fds_for_new_worker.sockfd = events[i].data.fd;
                /*新启动一个工作线程为sockfd服务*/
                pthread_create(&thread, NULL, worker, &fds_for_new_worker);

            }
            else
            {
                printf("something unexpected happened!\n");
            }
        }
    }

    close(listenfd);

    return 0;
}

图片 4

EPOLLONESHOT模式现象分析:我们继续使用上面的TCP客户端来测试,需要修改一下客户端的sleep时间改为3秒。工作流程就是:客户端第一次发送数据时服务器的接收缓冲区是有足够空间的,然后服务器的工作线程进入5秒的处理数据阶段;3秒后客户端继续发送新数据过来,但是工作线程还在处理数据,没办法立即接收新的数据。2秒后,客户端该线程数据处理完了,开始接收新的数据。可以观察到,我们客户端只使用了同一个线程去处理同一个客户端的请求,符合预期。

2、  从源代码构建OpenCV


epoll模型的优点

  • 支持一个进程打开大数目的socket描述符
  • IO效率不随FD数目增加而线性下降
  • 使用mmap加速内核与用户空间的消息传递

[编译器] > sudo apt-get
install build-essential

vim复制粘贴

y复制
普通模式中,yy复制游标所在的整行(3yy表示复制3行)
普通模式中,y^ 复制至行首,或y0。不含光标所在处字符。
普通模式中,y$ 复制至行尾。含光标所在处字符。
普通模式中,yw 复制一个单词。
普通模式中,y2w 复制两个单词。
普通模式中,yG 复制至文本末。
普通模式中,y1G 复制至文本开头。
p粘贴
普通模式中,p(小写)代表粘贴至光标后(下)
普通模式中,P(大写)代表粘贴至光标前(上)
d剪切


 

epoll的两种工作模式

  • LT(level triggered,水平触发模式)是缺省的工作方式,并且同时支持
    block 和 non-block
    socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。比如内核通知你其中一个fd可以读数据了,你赶紧去读。你还是懒懒散散,不去读这个数据,下一次循环的时候内核发现你还没读刚才的数据,就又通知你赶紧把刚才的数据读了。这种机制可以比较好的保证每个数据用户都处理掉了。

  • ET(edge-triggered,边缘触发模式)是高速工作方式,只支持no-block
    socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,等到下次有新的数据进来的时候才会再次出发就绪事件。简而言之,就是内核通知过的事情不会再说第二遍,数据错过没读,你自己负责。这种机制确实速度提高了,但是风险相伴而行。

1、安装OpenCV的依赖包

vim显示行号

:set nu


 

现在有这么一个场景:我是一个很忙的大老板,我有100个手机,手机来信息了,我的秘书就会告诉我“老板,你的手机来信息了。”我很生气,我的秘书就是这样子,每次手机来信息就只告诉我来信息了,老板赶紧去看。但是她从来不把话说清楚:到底是哪个手机来信息啊!我可有100个手机啊!于是,我只能一个一个手机去查看,来确定到底是哪几个手机来信息了。这就是IO复用中select模型的缺点!老板心想,要是秘书能把来信息的手机直接拿到我桌子上就好了,那么我的效率肯定大增(这就是epoll模型)。

安装完Python、与Python相关的软件包、cmake后就可构建OpenCV了。首先从

发表评论

电子邮件地址不会被公开。 必填项已用*标注