一、阻塞IO
最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。
当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。
常见的阻塞:wait、pause、sleep等函数;read或write某些文件时
优点:内核好实现、没有降低CPU的效率,让CPU整体处理效率提高,适合一路IO
也许有人会说,可以采用多线程+ 阻塞IO 来解决效率问题,但是由于在多线程 + 阻塞IO 中,每个socket对应一个线程,这样会造成很大的资源占用,并且尤其是对于长连接来说,线程的资源一直不会释放,如果后面陆续有很多连接的话,就会造成性能上的瓶颈。
二、非阻塞IO
当用户线程发起一个IO操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送IO操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
对于
非阻塞IO就有一个非常严重的问题
,在while循环中需要不断地去询问内核数据是否就绪,这样会导致CPU占用率非常高,因此一般情况下很少使用while循环这种方式来读取数据。
为什么要实现非阻塞?
实现IO多路复用
(如何实现非阻塞IO访问:O_NONBLOCK和fcntl
三、阻塞式IO的困境
测试/dev/input 下哪个是鼠标设备
滑动鼠标会输出信息即当前设备为鼠标设备
1、程序中读取键盘
int main(void)
{
// 读取键盘
// 键盘就是标准输入,stdin
char buf[100];
memset(buf, 0, sizeof(buf));
printf("before read.\n");
read(0, buf, 5);
printf("读出的内容是:[%s].\n", buf);
return 0;
}
2、程序中读取鼠标
int main(void)
{
// 读取鼠标
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);
return 0;
}
3、阻塞式同时读鼠标和键盘
int main(void)
{
// 读取鼠标
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;
}
结果:只能按顺序读取
4、解决办法
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
// 读取鼠标
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;
}
// 由于标准输入系统默认是打开的 所以只能通过fcntl来设置文件的属性
// 把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);
}
}
return 0;
}