linux非阻塞IO与阻塞IO的应用

1.为什么有阻塞IO
(1)常见的阻塞:wait、pause、sleep等函数,read和write某些特殊文件时
(2)阻塞式的好处,在阻塞等待的进程不消耗CPU运行,提高性能。

2.如何实现非阻塞IO访问
(1)O_NONBLOCK和fcntl

3.阻塞式IO的困境
(1)程序中读取键盘

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
        char buf[100];

        memset(buf,0,sizeof(buf));
        printf("before read\n");
        //读取键盘键盘就是标准输入,stdin
        read(0,buf,2);//read默认是阻塞的
        printf("读出的内容是:[%s]\n",buf);

        return 0;
}

(2)程序中读取鼠标
这里的鼠标输入在/dev/input/mouse1有的是在mouse0下面通过cat /dev/input/mouse0或者mouse1然后移动鼠标出现乱码则就是鼠标输入设备。

#include <string.h>
#include <unistd.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/mouse0",O_RDONLY);
        if(fd < 0)
        {
                perror("open");
                return -1;
        }
        memset(buf,0,sizeof(buf));
        printf("before read\n");
        //读取鼠标
        read(fd,buf,5);//read默认是阻塞的
        printf("读出的内容是:[%s]\n",buf);

		return 0;
}

(3)程序中同时读取键盘鼠标

#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
        int fd = -1;
        char buf[200];

        fd = open("/dev/input/mouse0",O_RDONLY);//打开鼠标
        if(fd < 0)
        {
                perror("open");
                return -1;
        }
        memset(buf,0,sizeof(buf));
        printf("before 鼠标 read\n");
        //读取鼠标
        read(fd,buf,5);//read默认是阻塞的
        printf("鼠标读出的内容是:[%s]\n",buf);

        memset(buf,0,sizeof(buf));
        printf("before 键盘 read\n");
        //读取键盘键盘就是标准输入,stdin
        read(0,buf,2);//read默认是阻塞的
        printf("键盘读出的内容是:[%s]\n",buf);
		return 0;
}

(4)问题分析
像这种情况就只能先读鼠标再读键盘这就是阻塞式的缺陷

4.并发式IO
(1)非阻塞式IO

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{


        int fd = -1;
        int ret = -1;
        int flag = -1;
        char buf[200];

        fd = open("/dev/input/mouse0",O_RDONLY|O_NONBLOCK);//打开鼠标
        if(fd < 0)
        {
                perror("open");
                return -1;
        }

        //把0号文件描述符(stdin)变成非阻塞式
        //int fcntl(int fd, int cmd, ... * arg * );
        flag = fcntl(0,F_GETFL);        //先获取原来的flag
        flag |= O_NONBLOCK;             //添加非阻塞属性
        fcntl(0,F_SETFL,flag);          //更新flag
        //上面三步将0这个文件描述符变为非阻塞式的

        while(1)
        {
                memset(buf,0,sizeof(buf));
                //printf("before 鼠标 read\n");
                //读取鼠标
                ret = read(fd,buf,5);//read默认是阻塞的
                if(ret > 0)
                {
                        printf("鼠标读出的内容是:[%s]\n",buf);
                }

                memset(buf,0,sizeof(buf));
				//printf("before 键盘 read\n");
                //读取键盘键盘就是标准输入,stdin
                ret = read(0,buf,5);//read默认是阻塞的
                if(ret > 0)
                {
                        printf("键盘读出的内容是:[%s]\n",buf);
                }
        }
        
	return 0;
}

5.多路复用IO
(1)何为IO多路复用(IO multiplexing)
(2)用在什么地方:多路非阻塞式IO
(3)select和poll(本身是阻塞式函数)

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
参数1:文件描述符的个数(文件IO中最大文件描述符+1)
参数2:要读的文件描述符的集合(相当于结构体,放多个需要读的文件描述符)
参数3:要写的多个文件描述符
参数4:看文件描述符是否出错
参数5:设置超时时间(如果这段事间一直没有IO事件发生就返回0)
返回值:返回发生了几次IO事件
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数1:多个文件描述符的集合
参数2:放入这个集合的文件描述符个数+1
参数3:超时时间
返回值:返回发生了几次IO事件

select函数实现

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>

int main(void)
{
        int fd = -1;
        int ret = -1;
        fd_set myset;
        char buf[200];
        struct timeval tm;

        fd = open("/dev/input/mouse0",O_RDONLY);//打开鼠标
        if(fd < 0)
        {
                perror("open");
                return -1;
        }

        //当前有两个fd,1个是fd,1个是0
        //处理myset
        FD_ZERO(&myset);//先清零
        FD_SET(fd,&myset);//设置fd
        FD_SET(0,&myset);//设置0这个fd
        tm.tv_sec = 10;//设置秒
        tm.tv_usec = 0;//设置微妙(两个秒数是相加的)
        //int select(int nfds, fd_set *readfds, fd_set *writefds,
        //fd_set *exceptfds, struct timeval *timeout);
        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));
                        //读取键盘键盘就是标准输入,stdin
                        read(0,buf,5);//read默认是阻塞的
                        printf("键盘读出的内容是:[%s]\n",buf);

                }
                else if(FD_ISSET(fd,&myset))
                {
                        //这里处理鼠标
                        memset(buf,0,sizeof(buf));
                        read(fd,buf,5);//read默认是阻塞的
                        printf("鼠标读出的内容是:[%s]\n",buf);

                }
        }

        return 0;
}

poll函数实现:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <poll.h>

int main(void)
{
        int fd = -1;
        int ret = -1;
        struct pollfd myfds[2] = {0};
        char buf[200];
        struct timespec tm;

        fd = open("/dev/input/mouse0",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;//等待读操作
        tm.tv_sec = 10000;
        //int poll(struct pollfd *fds, nfds_t nfds, int timeout);
        //ret = poll(myfds,2,tm.tv_sec);//2个文件描述符,10000毫秒
        ret = poll(myfds,fd,10000);//2个文件描述符,10000毫秒,这里写fd文件运行>直接退出
        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));
                        //读取键盘键盘就是标准输入,stdin
                        read(0,buf,5);//read默认是阻塞的
                        printf("键盘读出的内容是:[%s]\n\r",buf);

                }
                if(myfds[1].events == myfds[1].revents)
                {
                        //这里处理鼠标
                        memset(buf,0,sizeof(buf));
                        read(fd,buf,50);//read默认是阻塞的
                        printf("鼠标读出的内容是:[%s]\n",buf);

                }
        }

        return 0;
}

(4)外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO

6.异步通知(异步IO)
(1)异步IO就是操作系统用软件实现的一套中断系统。
(2)异步IO的工作方法:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
(3)涉及函数(fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN))、signal或sigaction(SIGIO)
(4)代码实现

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int mousefd = -1;


//异步通知函数,绑定SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{
        char buf[200] = {0};

        if(sig != SIGIO)
                return;

        //读取鼠标
        read(mousefd,buf,5);//read默认是阻塞的
        printf("鼠标读出的内容是:[%s]\n",buf);

}

int main(void)
{
        char buf[200];
        int flag = -1;

        mousefd = open("/dev/input/mouse0",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));
                //读取键盘键盘就是标准输入,stdin
                read(0,buf,5);//read默认是阻塞的
                printf("键盘读出的内容是:[%s]\n",buf);
        }


        return 0;
}

7.存储映射IO
(1)mmap函数
(2)LCD显示和IPC之共享内存
(3)存储映射的特点:共享而不是复制,减少内存操作。处理大文件时效率高,小文件不划算

void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值