高级IO --- 阻塞与非阻塞IO

一、阻塞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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值