IO多路复用

操作方式: 创建文件描述符

                    添加文件描述符

                    通知内核开始监测

                    根据返回的结果做对应操作

应用场景:1. 构建并发服务器,使用IO多路复用监测多个客户端套接字
                  2. 使用io多路复用监测多个IO所对应的通信(如:网络、串口、can....)
                  3. 在阻塞io中,进行超时监测

一、select

1.创建文件描述符

fd_set rdfds;
fd_set tmpfds;

2.  添加文件描述符

//清空集合
	FD_ZERO(&rdfds);
	//添加关注的文件描述符到集合中
	FD_SET(0, &rdfds);
	maxfd = 0 > maxfd ? 0 : maxfd;
	FD_SET(fifofd, &rdfds);
	maxfd = fifofd > maxfd ? fifofd : maxfd;

3.通知内核开始监测(select)

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

功能:监听文件描述符集合

参数: nfds:监测的文件描述符上限值(最大文件描述符的值+1)
        readfds:读文件描述符集合
        writefds:写文件描述符集合
        exceptfds:异常条件的描述符集合
        timeout:设置超时时间,NULL:一直等待

返回值:成功返回产生事件文件描述符个数,失败返回-1 ,定时时间到达仍没有事件产生返回0 

void FD_CLR(int fd, fd_set *set);将fd从文件描述符集合中清除

int  FD_ISSET(int fd, fd_set *set);判断文件描述符fd是否仍在文件描述符集合中

void FD_SET(int fd, fd_set *set);将fd加入文件描述符集合中

void FD_ZERO(fd_set *set);文件描述符集合清0 

tmpfds = rdfds;
		//开始监测集合中的io
		int cnt = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
		if (cnt < 0)
		{
			perror("fail select");
			return -1;
		}

4.   根据返回的结果做对应操作

if (FD_ISSET(0,&tmpfds))
		{
			memset(buff, 0, sizeof(buff));
			fgets(buff, sizeof(buff), stdin);
			printf("STDIN : %s\n", buff);
		}
		if (FD_ISSET(fifofd, &tmpfds))
		{
			memset(buff, 0, sizeof(buff));
			read(fifofd, buff, sizeof(buff));
			printf("FIFO : %s\n", buff);
		}

 缺点:1.select监听文件描述符最大个数为1024    (数组)     O(n)
          2.select监听的文件描述符集合在用户层,需要应用层和内核层互相传递数据
          3.select需要循环遍历一次才能找到产生的事件
          4.select只能工作在水平触发模式(低速模式)无法工作在边沿触发模式(高速模式)

二、poll

 缺点:   1.poll监测文件描述符不受上限限制  (链表)   O(n)
            2.poll监听的文件描述符集合在用户层,需要内核层向用户层传递数据
            3.poll需要循环遍历一次才能找到产生的事件
            4.poll只能工作在水平触发模式(低速模式)无法工作在边沿触发模式(高速模式)

三、epoll

1、epoll_create 创建epoll文件描述符集合

int epoll_create(int size);

功能:创建一个监听事件表(内核中)

参数:size:监听事件最大个数

返回值:成功返回非负值:表示epoll事件表对象(句柄),失败返回-1 

int epfds = epoll_create(2);
	if (-1 == epfds)
	{
		perror("fail epoll_create");
		return -1;
	}

2、epoll_ctl  添加关注的文件描述符

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

功能:在监听事件表中新增一个事件

参数: epfd:事件表文件描述符 
        op:      EPOLL_CTL_ADD        新增事件
                   EPOLL_CTL_MOD        修改事件 
                    EPOLL_CTL_DEL        删除事件
        fd:文件描述符 

events:事件相关结构体

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

typedef union epoll_data {
           void        *ptr;
           int          fd;
           uint32_t     u32;
           uint64_t     u64;
        } epoll_data_t;

struct epoll_event {
           uint32_t     events;      /* Epoll events */
           epoll_data_t data;        /* User data variable */
        };

events:              EPOLLIN        读事件

                          EPOLLOUT    写事件

EPOLLET        边沿触发    
        LT            水平触发

struct epoll_event ev;
	ev.events = event;
	ev.data.fd = fd;
	int ret = epoll_ctl(epfds, EPOLL_CTL_ADD, fd, &ev);
	if (-1 == ret)
	{
		perror("fail epoll_ctl add");
		return -1;
	}

3、epoll_wait 监控io事件

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

功能:监听事件表中的事件,并将产生的事件存放到结构体数组中

参数:epfd:事件表文件描述符
        events:存放结果事件结构体数组空间首地址 
        maxevents:最多存放事件个数
        timeout:超时时间,-1:阻塞等待直到有事件发生 

返回值:成功返回产生事件个数,失败返回-1 ,超时时间到达没有事件发生返回0 

int cnt = epoll_wait(epfds, evs, 2, -1);
		if (cnt < 0)
		{
			perror("fail epoll_wait");
			return -1;
		}

4.根据返回值做对应的操作

for (int i = 0; i < cnt; i++)
		{
			if (0 == evs[i].data.fd)
			{
				memset(buff, 0, sizeof(buff));
				fgets(buff, sizeof(buff), stdin);
				printf("STDIN: %s\n", buff);
			}
			else if (fifofd == evs[i].data.fd)
			{
				memset(buff, 0, sizeof(buff));
				ssize_t size = read(evs[i].data.fd, buff, sizeof(buff));
				if (size <= 0)
				{
					epoll_del_fd(epfds, evs[i].data.fd);
					close(evs[i].data.fd);
					continue;
				}
				printf("FIFO: %s\n", buff);
			}
		}

5、epoll_ctl  从事件集合中删除完成的文件描述符

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

int ret = epoll_ctl(epfds, EPOLL_CTL_DEL, fd, NULL);
	if (-1 == ret)
	{
		perror("fail epoll_ctl del");
		return -1;
	}

优点:    1.epoll创建内核事件表,不受到文件描述符上限限制    (红黑树)  O(logn)        
            2.epoll监听的事件表在内核中,直接在内核中监测事件效率高
            3.epoll会直接获得产生事件的文件描述符的信息,而不需要遍历检测 
            4.epoll既能工作在水平触发模式,也能工作在边沿触发模式

你可能感兴趣的:(服务器,数据库,运维)