并发式IO的解决方案---非阻塞式、多路复用和异步通知(异步IO)

    xiaoxiao2021-04-14  72

    ---非阻塞式IO 阻塞式读取键盘和鼠标: // 读取鼠标 int fd = -1; char buf[200]; fd = open("/dev/input/mouse1", O_RDONLY); if (fd < 0) { perror("open:"); return -1; } memset(buf, 0, sizeof(buf)); printf("before 鼠标 read.\n"); read(fd, buf, 50); printf("鼠标读出的内容是:[%s].\n", buf); // 读键盘 memset(buf, 0, sizeof(buf)); printf("before 键盘 read.\n"); read(0, buf, 5); printf("键盘读出的内容是:[%s].\n", buf); return 0; 实现非阻塞IO访问:O_NONBLOCK和fcntl int fcntl(int fd, int cmd, ... /* arg */ ); F_GETFL (void)               Get the file access mode and the file status flags; arg is ignored. F_SETFL (int)               Set the file status flags to  the  value  specified  by  arg.   File  access  mode               (O_RDONLY,  O_WRONLY,  O_RDWR)  and  file  creation  flags (i.e., O_CREAT, O_EXCL,               O_NOCTTY, O_TRUNC) in arg are ignored.  On Linux this command can change only  the               O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags. 属性: O_NONBLOCK //非阻塞 O_ASYNC //接收异步IO int fd = -1; int flag = -1; char buf[200]; int ret = -1; fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK); if (fd < 0) { perror("open:"); return -1; } // 把0号文件描述符(stdin)变成非阻塞式的 flag = fcntl(0, F_GETFL); // 先获取原来的flag flag |= O_NONBLOCK; // 添加非阻塞属性 fcntl(0, F_SETFL, flag); // 更新flag // 这3步之后,0就变成了非阻塞式的了 while (1) { // 读鼠标 memset(buf, 0, sizeof(buf)); //printf("before 鼠标 read.\n"); ret = read(fd, buf, 50); if (ret > 0) { printf("鼠标读出的内容是:[%s].\n", buf); } // 读键盘 memset(buf, 0, sizeof(buf)); //printf("before 键盘 read.\n"); ret = read(0, buf, 5); if (ret > 0) { printf("键盘读出的内容是:[%s].\n", buf); } } ---多路复用IO select和poll 外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO ---select函数介绍 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); //nfds:多路阻塞式IO中最大的文件描述符+1//*readfds:要读的IO//*writefds:要写的IO//*exceptfds:出现错误的IO//*timeout:超时时间,在超时时间内,有IO激活函数,则立刻返回,没有IO激活,则在到达超时时间时返回//struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; //返回值:0-超时返回 >0-返回的是IO的返回路数 -1-返回错误 select的几个宏函数: void FD_CLR(int fd, fd_set *set); //将fd从set里面拿出去 clearO(∩_∩)O~ int FD_ISSET(int fd, fd_set *set); //判断fd有没有被置位,返回值是正的,说明这个fd的IO发生了,负的就没有发生 void FD_SET(int fd, fd_set *set); //将fd添加进set里面去 void FD_ZERO(fd_set *set); //将所有的fd_set全部清除出去 代码实践:用select函数实现同时读取键盘鼠标 int fd = -1, ret = -1; char buf[200]; fd_set myset; struct timeval tm; fd = open("/dev/input/mouse1", O_RDONLY); if (fd < 0) { perror("open:"); return -1; } // 当前有2个fd,一共是fd一个是0 // 处理myset FD_ZERO(&myset); FD_SET(fd, &myset); //鼠标的 FD_SET(0, &myset); //键盘的 tm.tv_sec = 10; tm.tv_usec = 0; ret = select(fd+1, &myset, NULL, NULL, &tm); //如果两个都没有输入,程序就会在这阻塞住 if (ret < 0) { perror("select: "); return -1; } else if (ret == 0) { printf("超时了\n"); } else { // 等到了一路IO,然后去监测到底是哪个IO到了,处理之 if (FD_ISSET(0, &myset)) { // 这里处理键盘 memset(buf, 0, sizeof(buf)); read(0, buf, 5); printf("键盘读出的内容是:[%s].\n", buf); } if (FD_ISSET(fd, &myset)) { // 这里处理鼠标 memset(buf, 0, sizeof(buf)); read(fd, buf, 50); printf("鼠标读出的内容是:[%s].\n", buf); } } return 0; ---poll函数介绍 int poll(struct pollfd *fds, nfds_t nfds, int timeout); //使用前必须先设置 struct pollfd * fds struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ //是内核设置的 }; events常用宏         POLLIN    There is data to read. //FD要读 POLLOUT     Writing now will not block.//FD要写 如果有事件发生,要判断是哪个IO发生的,就去判断每个IO的 events和revents是否相等 ,若相等就说明此IO发生 返回值和select一样; 代码实践:用poll函数实现同时读取键盘鼠标 // 读取鼠标 int fd = -1, ret = -1; char buf[200]; struct pollfd myfds[2] = {0}; fd = open("/dev/input/mouse1", O_RDONLY); if (fd < 0) { perror("open:"); return -1; } // 初始化我们的pollfd myfds[0].fd = 0; // 键盘 myfds[0].events = POLLIN; // 等待读操作 myfds[1].fd = fd; // 鼠标 myfds[1].events = POLLIN; // 等待读操作 ret = poll(myfds, fd+1, 10000); if (ret < 0) { perror("poll: "); return -1; } else if (ret == 0) { printf("超时了\n"); } else { // 等到了一路IO,然后去监测到底是哪个IO到了,处理之 if (myfds[0].events == myfds[0].revents) { // 这里处理键盘 memset(buf, 0, sizeof(buf)); read(0, buf, 5); printf("键盘读出的内容是:[%s].\n", buf); } if (myfds[1].events == myfds[1].revents) { // 这里处理鼠标 memset(buf, 0, sizeof(buf)); read(fd, buf, 50); printf("鼠标读出的内容是:[%s].\n", buf); } } return 0; ---异步通知(异步IO) 异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。 3.6.6.2、涉及的函数: (1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN) (2)signal或者sigaction(SIGIO) 3.6.3.代码实践 int mousefd = -1;// 绑定到SIGIO信号,在函数内处理异步通知事件void func(int sig){ char buf[200] = {0}; if (sig != SIGIO) return; read(mousefd, buf, 50); printf("鼠标读出的内容是:[%s].\n", buf);}int main(void){ // 读取鼠标 char buf[200]; int flag = -1; mousefd = open("/dev/input/mouse1", O_RDONLY); if (mousefd < 0) { perror("open:"); return -1; } // 把鼠标的文件描述符设置为可以接受异步IO flag = fcntl(mousefd, F_GETFL); flag |= O_ASYNC;        //接受异步IO fcntl(mousefd, F_SETFL, flag); // 把异步IO事件的接收进程设置为当前进程 fcntl(mousefd, F_SETOWN, getpid()); // 注册当前进程的SIGIO信号捕获函数 signal(SIGIO, func); // 读键盘 while (1) { memset(buf, 0, sizeof(buf)); //printf("before 键盘 read.\n"); read(0, buf, 5); printf("键盘读出的内容是:[%s].\n", buf); } return 0;} ---存储映射IO mmap函数 LCD显示和IPC之共享内存 存储映射IO的特点 (1)共享而不是复制,减少内存操作 (2)处理大文件时效率高,小文件不划算
    转载请注明原文地址: https://ju.6miu.com/read-669977.html

    最新回复(0)