嵌入式学习笔记-linux应用编程和网络编程-3.6 高级IO(相对于第一节的普通文件io)

一、非阻塞IO
1、阻塞与非阻塞
2、为什么有阻塞式
  • 常见的阻塞:wait、pause、sleep等函数;read或write某些文件时
  • 阻塞式的好处(cpu跑去运行其他进程,不被浪费就好)(类似点餐了在旁边候餐)
3、非阻塞
  • 为什么要实现非阻塞
  • 如何实现非阻塞IO访问:O_NONBLOCK(未打开)和fcntl(已经打开)
二、阻塞式IO的困境
1、程序中读取键盘
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	char buf[200];					// 键盘就是标准输入,stdin
	
	memset(buf, 0, sizeof(buf));
	printf("before 键盘 read.\n");
	read(0, buf, 5);				//在此处堵塞住,等待键盘输入	但键盘输入在缓冲区,回车才算输入完成
	printf("键盘读出的内容是:[%s].\n", buf);
	return 0;
}
2、程序中读取鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	// 读取鼠标	不是标准输入设备,只能自己打开 ,键盘为标准输入fd = 0
	int fd = -1;
	char buf[200];
	
	fd = open("/dev/input/mouse1", O_RDONLY);	///dev/input/mouse1是鼠标的路径
	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、程序中同时读取键盘和鼠标
  • 只有按照程序的顺序先读鼠标再读键盘
#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;
	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、问题分析
  • 这就是堵塞式缺点的一方面
三、并发式IO的解决方案
1、非阻塞式IO(类似使用轮询的方式)
#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;
	}
	
	// 把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;
}
2、多路复用IO
3、异步通知(异步IO)
四、IO多路复用原理
1、何为IO多路复用
  • IO multiplexing
  • 用在什么地方?多路非阻塞式IO。
  • select(监听多路)和poll
  • 外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO(a和b都是堵塞式的,但select以非堵塞式的方式监听a和b,两者任一来数据,则会唤醒select函数,类似共享中断,两者都能触发select函数,其原理和非堵塞式io很相似,select是用让出cpu的方式来轮询,没有while轮询那么浪费cpu)
2、select函数介绍
  • int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • nfds代表监听范围,=最大的文件描述符+1,因为是从0开始的
  • fd_set *readfds 要读的文件
  • fd_set *writefds 要写的文件
  • fd_set *exceptfds 出错的文件
  • struct timeval *timeout为超时时间
  • void FD_CLR(int fd, fd_set *set); 去除fd_set *se中的文件
  • int FD_ISSET(int fd, fd_set *set) ; 判断是否在fd_set *set里面
  • void FD_SET(int fd, fd_set *set); 添加文件到fd_set *se
  • void FD_ZERO(fd_set *set); 清空fd_set *se
3、poll函数介绍
  • int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • struct pollfd *fds 类似于fd_set *se
  • nfds 代表监听范围,=最大的文件描述符+1,因为是从0开始的
  • int timeout 超时时间,单位是毫秒
  • struct pollfd {
    int fd; /* file descriptor /
    short events; /
    requested events /
    short revents; /
    returned events */
    };
五、IO多路复用实践
1、用select函数实现同时读取键盘鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>

int main(void)
{
	// 读取鼠标
	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;
}
2、用poll函数实现同时读取键盘鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>

int main(void)
{
	// 读取鼠标
	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(类似中断)
1、何为异步IO
  • 几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统
  • 异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
2、涉及的函数:
  • fcntl(F_GETFL、F_SETFL、O_ASYNC(可接受异步io)、F_SETOWN(设置通知谁,当前进程))
  • signal或者sigaction(SIGIO)
3、代码实践
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

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;
	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
1、mmap函数
  • void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • 用于内存映射
2、LCD显示和IPC之共享内存
3、存储映射IO的特点
  • 共享而不是复制,减少内存操作
  • 处理大文件时效率高,小文件不划算

——资料来源于朱老师物联网大讲堂

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值