io多路复用理解

linux IO模型之 多路复用

  1. 进程中调用poll和select操来查询打开的I/O设备文件是否就绪(有资源)

  2. 设备驱动操作集合中的 unsigned int (*poll) (struct file *, poll_table *)被调用

应用程序调用 select -> 内核 -> driver -> poll

  1. select 函数的查询文件时候就绪, 调用一次poll
    如果没有就绪 , 函数睡眠, 被唤醒后,在查询一遍,此时再调用一次poll
    因此: select 调用后, 驱动中要调用2次poll 函数

  2. 通过poll_wait可以向驱动向poll_table结构添加一个等待队列
    static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

    poll_wait(filp,&wq,poll_table);

  3. 驱动的poll函数应该返回设备的当前状态
    POLLIN,POLLOUT,POLLRDNORM,POLLERR

IO模式一般分为同步IO和异步IO. 同步IO会阻塞进程, 异步IO不会阻塞进程. 目前linux上大部分用的是同步IO, 异步IO在linux上目前还不成熟, 不过windows的iocp算是真正的异步IO。
同步IO又分为阻塞IO, 非阻塞IO, IO多路复用. What? 同步IO明明会阻塞进程,为什么也包括非阻塞IO? 因为非阻塞IO虽然在请求数据时不阻塞, 但真正数据来临时,也就是内核数据拷贝到用户数据时, 此时进程是阻塞的.
那么这些IO模式的区别分别是什么? 接下来举个小例子来说明. 假设你现在去女生宿舍楼找自己的女神, 但是你只知道女神的手机号,并不知道女神的具体房间 先说同步IO的情况,
1.阻塞IO, 给女神发一条短信, 说我来找你了, 然后就默默的一直等着女神下楼, 这个期间除了等待你不会做其他事情, 属于备胎做法.
2.非阻塞IO, 给女神发短信, 如果不回, 接着再发, 一直发到女神下楼, 这个期间你除了发短信等待不会做其他事情, 属于专一做法.
3.IO多路复用, 是找一个宿管大妈来帮你监视下楼的女生, 这个期间你可以些其他的事情. 例如可以顺便看看其他妹子,玩玩王者荣耀, 上个厕所等等. IO复用又包括 select, poll, epoll 模式. 那么它们的区别是什么?
3.1select大妈 每一个女生下楼, select大妈都不知道这个是不是你的女神, 她需要一个一个询问, 并且select大妈能力还有限, 最多一次帮你监视1024个妹子
3.2poll大妈不限制盯着女生的数量, 只要是经过宿舍楼门口的女生, 都会帮你去问是不是你女神
3.3epoll大妈不限制盯着女生的数量, 并且也不需要一个一个去问. 那么如何做呢? epoll大妈会为每个进宿舍楼的女生脸上贴上一个大字条,上面写上女生自己的名字, 只要女生下楼了, epoll大妈就知道这个是不是你女神了, 然后大妈再通知你.
上面这些同步IO有一个共同点就是, 当女神走出宿舍门口的时候, 你已经站在宿舍门口等着女神的, 此时你属于阻塞状态
接下来是异步IO的情况 你告诉女神我来了, 然后你就去王者荣耀了, 一直到女神下楼了, 发现找不见你了, 女神再给你打电话通知你, 说我下楼了, 你在哪呢? 这时候你才来到宿舍门口. 此时属于逆袭做法 。
poll用法:
`#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define N 128

#define LEDON _IO(‘L’,0)
#define LEDOFF _IO(‘L’,1)

int main(int argc, char * argv[])
{
int fd;
int ret ;
int num;
char buf[N] = “hello driver”;
fd = open("/dev/mychar",O_RDWR|O_NONBLOCK);
if(fd < 0)
{
perror(“open”);
exit(-1);
}
printf(“fd=%d\n”,fd);
while(1)
{

    printf("please input you choice:1: write 2:read\n");
    scanf("%d",&num);
    switch(num)
    {
        case 1:
            strcpy(buf,"hello dirver");
            ret = write(fd,buf,N);
            if(ret < 0)
            {
                perror("read");
                exit(-1);
            }
            printf("write data ok\n");
            break ;
        case 2:
            ret = read(fd,buf,N);
            if(ret < 0)
            {
                perror("read");
                exit(-1);
            }
            printf("buf=%s\n",buf);
            break ;
        default : break ;
    }
}
close(fd);

return 0 ;

}
test.c代码:`#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define N 128

#define LEDON _IO(‘L’,0)
#define LEDOFF _IO(‘L’,1)

int main(int argc, char * argv[])
{
int fd;
int ret ;
int num;
char buf[N] = “hello driver”;
int i;
fd_set rdfs;
fd = open("/dev/mychar",O_RDWR);
if(fd < 0)
{
perror(“open”);
exit(-1);
}
printf(“fd=%d\n”,fd);
while(1)
{
FD_ZERO(&rdfs);
FD_SET(0,&rdfs);
FD_SET(fd,&rdfs);
ret = select(fd+1,&rdfs,NULL,NULL,NULL);
if(ret < 0)
{
perror(“select”);
exit(-1);
}
for(i=0;i<=fd;i++)
{
if(FD_ISSET(i,&rdfs))
{
if(i == 0 )
{
fgets(buf,N,stdin);
printf(“stdin buf=%s\n”,buf);
}
else if (i == fd)
{
ret = read(fd,buf,N);
if(ret < 0)
{
perror(“read”);
exit(-1);
}
printf(“driver buf=%s\n”,buf);
}
}
}
}
close(fd);

return 0 ;

}
`

linux IO模型之 信号驱动的异步I/O

  1. 应用程序要捕捉SIGIO信号

    signal(SIGIO,sighandler);

  2. 应用程序要指定进程为文件的属主
    fcntl(fd, F_SETOWN, getpid());
    把进程的进程号 getpid() 赋值给 struct fown_struct f_owner;
    file->f_owner = getpid();

  3. 应用程序通过fcntl函数在设备中设置FASYNC标志, 让驱动调用一次 fasync

    oflags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, oflags | FASYNC);
    应用程序fcntl -> 内核 -> driver -> fasync

  4. 驱动中要实现 fasync
    int (*fasync) (int, struct file *, int);
    驱动中fasync_helper使用这个函数来安装一个异步队列

/*

  • fasync_helper() is used by almost all character device drivers
  • to set up the fasync queue, and for regular files by the file
  • lease code. It returns negative on error, 0 if it did no changes
  • and positive if it added/deleted the entry.
    */
    int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
    fd : 是应用程序的fd ,fasync的第一个参数
    filp : 是fasync的第二个参数
    on :值为真, 表示安装一个异步队列,
    值为假, 表示卸载一个异步队列,
    fapp: 二级指针, 传参一级指针的地址

成功返回0
失败 负数

  1. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
    void kill_fasync(struct fasync_struct **fp, int sig, int band)

linux 的中断机制

  1. 申请中断号并注册中断处理函数 request_irq

static int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
参数:
irq : 中断号
对于我们的平台是sun50iw2p1
vi arch/arm64/boot/dts/sun50iw2p1.dtsi
查看interrupts 这个关键字的描述信息

handler: 中断处理函数名
typedef irqreturn_t (*irq_handler_t)(int, void *);

flags : 标志如下;

#define IRQF_DISABLED 0x00000020 // 执行中断处理函数是, 会禁止其他的中断
#define IRQF_SHARED 0x00000080 // 共享中断
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_FORCE_RESUME 0x00008000
#define IRQF_NO_THREAD 0x00010000
#define IRQF_EARLY_RESUME 0x00020000

#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001 // 上升沿触发中断
#define IRQF_TRIGGER_FALLING 0x00000002 // 下降沿触发中断
#define IRQF_TRIGGER_HIGH 0x00000004 // 高电平触发中断
#define IRQF_TRIGGER_LOW 0x00000008 // 低电平触发中断
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW |
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010

name : 中断的名称 , 对于中断号的描述

dev : 在共享中断中会用到

  1. 可以通过命令来查看系统的中断号资源
    cat /proc/interrupts
    CPU0 CPU1 CPU2 CPU3
    27: 19336 16966 13452 16623 GIC arch_timer
    32: 3101 0 0 0 GIC uart0
    38: 0 0 0 0 GIC twi0
    39: 0 0 0 0 GIC twi1
    43: 0 0 0 0 GIC PIN_GRP
    49: 0 0 0 0 GIC PIN_GRP
    50: 0 0 0 0 GIC sunxi_timer
    55: 0 0 0 0 GIC PIN_GRP
    62: 0 0 0 0 GIC sunxikbd
    72: 0 0 0 0 GIC 1f00000.rtc
    77: 0 0 0 0 GIC PIN_GRP
    82: 0 0 0 0 GIC 1c02000.dma-controller
    90: 0 0 0 0 GIC cedar_dev
    92: 2228 0 0 0 GIC sunxi-mmc
    97: 0 0 0 0 GIC spi0
    98: 0 0 0 0 GIC spi1
    101: 0 0 0 0 GIC mdfs
    104: 0 0 0 0 GIC ehci_hcd:usb1
    105: 0 0 0 0 GIC ohci_hcd:usb5
    106: 0 0 0 0 GIC ehci_hcd:usb2
    107: 0 0 0 0 GIC ohci_hcd:usb6
    108: 21 0 0 0 GIC ehci_hcd:usb3
    109: 0 0 0 0 GIC ohci_hcd:usb7
    110: 0 0 0 0 GIC ehci_hcd:usb4
    111: 0 0 0 0 GIC ohci_hcd:usb8
    114: 0 0 0 0 GIC 1c30000.eth
    118: 26038 0 0 0 GIC dispaly
    125: 0 0 0 0 GIC DE-Interlace
    279: 0 0 0 0 - 1c0f000.sdmmc cd
    IPI0: 3502 3941 2159 1968 Rescheduling interrupts
    IPI1: 40 133 162 164 Function call interrupts
    IPI2: 1 5 0 2 Single function call interrupts
    IPI3: 0 0 0 0 CPU stop interrupts
    IPI4: 0 0 0 0 Timer broadcast interrupts

  2. 释放中断 free_irq
    void free_irq(unsigned int irq, void *dev_id)

irq :要释放的中断号

dev_id :共享中断时使用, 一般为NULL

linux 中断底半部机制之 work_struct

  1. 定义一个work_struct
    struct work_struct mywork

  2. 初始化一个tasklet , INIT_WORK()

#define INIT_WORK(_work, _func) \

INIT_WORK(&my_wq, (void *) my_wq_func);

函数的类型:
定义一个处理函数
void my_wq_func(unsigned long) ;

  1. 调度工作队列执行
    schedule_work(&my_wq);
    领卓教育
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值