• 上一节分析了poll的原理和实现,这节分析它的孪生兄弟select。 先看一下select的函数原型。 man 2 select int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *time...

     

    上一节分析了poll的原理和实现,这节分析它的孪生兄弟select。

    先看一下select的函数原型。

    man 2 select
    int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

     

    #undef __FD_SETSIZE
    #define __FD_SETSIZE	1024
    
    /*fd_set集合可以通过一些宏由人为来操作,总共1024个bit,即代表select最多可监控1024个文件的状态 */
    typedef struct {
    	unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
    } __kernel_fd_set;
    
    
    typedef __kernel_fd_set		fd_set;
    
    void FD_CLR(int fd, fd_set *set);        /* 将一个给定的文件描述符从集合中删除*/    
    int  FD_ISSET(int fd, fd_set *set);      /* 检查集合中指定的文件描述符是否可以读写 */
    void FD_SET(int fd, fd_set *set);        /* 将一个给定的文件描述符加入集合之中 */
    void FD_ZERO(fd_set *set);            /* 清空集合FD_ZERO(fd_set *) */
    
    
    
    
    
    第一个参数maxfdp1指定待测试的描述字个数
    它的值是待测试的最大描述字加1(因此把该参数命名为maxfdp1),描述字0、1、2…maxfdp1-1均将被测试。 因为文件描述符是从0开始的,可以传入NULL值,表示不关心任何文件的读变化。。
    
    
    fd_set *readset
    fd_set *writeset
    fd_set *exceptset
    中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件的描述字。  
    如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符。
    
    struct timeval {
    	__kernel_time_t		tv_sec;		    /* seconds */
    	__kernel_suseconds_t	tv_usec;	/* microseconds */
    };
    struct timeval * timeout
    这个参数有三种可能
    1.永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。 
    2.等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。 
    3.根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。
    

     

     

    和poll一样select也是做查询使用的,select中在timeout参数为NULL时,比表示select以阻塞方式,必须等到监控文件的读写或异常时间发送才可以退出。在timeout重的两个时间参数都为0时,和poll就一样了,即变成一个纯粹的非阻塞函数,进入系统调用,查询一遍监控文件状态,如果有可读或可写或异常的则返回大于0的值,否则不睡眠,直接返回。第三种就是timeout结构里的事件大于0,即调用select先查询一遍,如果有可读或可写或异常的则返回大于0的值,否则睡眠timeout事件。当然,这里的timeout中小于一次调度的时间,就只能用来不停的查询了,不能用于睡眠了。

     

    接下来我们来分析一下select的实现

    SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
    		fd_set __user *, exp, struct timeval __user *, tvp)
    {
    	struct timespec end_time, *to = NULL;
    	struct timeval tv;
    	int ret;
    
    	if (tvp) {
    		if (copy_from_user(&tv, tvp, sizeof(tv)))    /* 如果时间不为NULL,则从应用空间拷贝到内核空间 */
    			return -EFAULT;
            /* 对传入的时间,解析成struct timespec格式 */
    		to = &end_time;
    		if (poll_select_set_timeout(to,
    				tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
    				(tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
    			return -EINVAL;
    	}
        
            /* 处理核心的select函数 */
    	ret = core_sys_select(n, inp, outp, exp, to);
            /*  如果不是超时返回的,则说明超时时间还没到, 则拷贝还剩的时间到用户空间的timeval中*/ */
    	ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
    
    	return ret;
    }

     

    typedef struct {
    	unsigned long *in, *out, *ex;
    	unsigned long *res_in, *res_out, *res_ex;
    } fd_set_bits;
    
    
    /*
     * We can actually return ERESTARTSYS instead of EINTR, but I'd
     * like to be certain this leads to no problems. So I return
     * EINTR just for safety.
     *
     * Update: ERESTARTSYS breaks at least the xview clock binary, so
     * I'm trying ERESTARTNOHAND which restart only when you want to.
     */
    int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
    			   fd_set __user *exp, struct timespec *end_time)
    {
    	fd_set_bits fds;
    	void *bits;
    	int ret, max_fds;
    	unsigned int size;
    	struct fdtable *fdt;
    	/* Allocate small arguments on the stack to save memory and be faster */
        /* 正如上面注释所说,在参数较少的情况下,使用局部变量栈内存可以提高效率 64 = 256 / 4  */
    	long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
    
    	ret = -EINVAL;
    	if (n < 0)
    		goto out_nofds;
    
    	/* max_fds can increase, so grab it once to avoid race */
    	rcu_read_lock();            
    	fdt = files_fdtable(current->files);        /* 获取当前进程的文件描述符表 */
    	max_fds = fdt->max_fds;                    /* 并得到当前进程最大打开的文件数 */
    	rcu_read_unlock();
    	if (n > max_fds)      /* 如果传进来要检查的文件个数大于最大打开的,则只检查最多打开的文件数   */
    		n = max_fds;
    
    	/*
    	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
    	 * since we used fdset we need to allocate memory in units of
    	 * long-words. 
         * 以一个文件描述符占一bit来计算,传递进来的这些fd_set需要用掉多少个long words
    	 */
    	size = FDS_BYTES(n);
    	bits = stack_fds;
    	if (size > sizeof(stack_fds) / 6) {    /* 每个fd_set_bits占用6个指针(指针和long字节数相等,如果默认的256个字节的不够,则用malloc申请,如果足够则就用栈中的) */
    		/* Not enough space in on-stack array; must use kmalloc */
    		ret = -ENOMEM;
    		bits = kmalloc(6 * size, GFP_KERNEL);
    		if (!bits)
    			goto out_nofds;
    	}
    
        /* 对比上面的结构体原型就知道这里是给每个指针分配起始地址 */
    	fds.in      = bits;
    	fds.out     = bits +   size;
    	fds.ex      = bits + 2*size;
    	fds.res_in  = bits + 3*size;
    	fds.res_out = bits + 4*size;
    	fds.res_ex  = bits + 5*size;
    
            /* 如果用户空间有传查询文件的,读或写或异常情况,则把其拷贝到内核空间,
             * 如果传入的是NULL,则把fds中的该项清0,如果三个都是NULL,则表示什么都不查询,则直接退出*/
    	if ((ret = get_fd_set(n, inp, fds.in)) ||
    	    (ret = get_fd_set(n, outp, fds.out)) ||
    	    (ret = get_fd_set(n, exp, fds.ex)))
    		goto out;
    
        /* 清0,fds的返回空间 */
    	zero_fd_set(n, fds.res_in);
    	zero_fd_set(n, fds.res_out);
    	zero_fd_set(n, fds.res_ex);
    
        /* 执行select,分贝传入 最大文件描述+1,保存有查询的文件状态地址的fds,超时时间 */
    	ret = do_select(n, &fds, end_time);
    
        /* 错误 */
    	if (ret < 0)
    		goto out;
        /* 判断是不是收到了信号,如果是,表示该系统调用被信号打断,返回-ERESTARTNOHAND,表明要求重新这个系统调用 */
    	if (!ret) {
    		ret = -ERESTARTNOHAND;
    		if (signal_pending(current))
    			goto out;
    		ret = 0;
    	}
    
        /* 查询结果,返回给用户空间 */
    	if (set_fd_set(n, inp, fds.res_in) ||
    	    set_fd_set(n, outp, fds.res_out) ||
    	    set_fd_set(n, exp, fds.res_ex))
    		ret = -EFAULT;
    
    out:
    	if (bits != stack_fds)
    		kfree(bits);        /* 如果256个字节不够,动态申请要在这里用完后释放掉 */
    out_nofds:
    	return ret;
    }

     

     

     

    int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
    {
    	ktime_t expire, *to = NULL;
    	struct poll_wqueues table;
    	poll_table *wait;
    	int retval, i, timed_out = 0;
    	unsigned long slack = 0;
    	unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;/* 当前网络是不是在忙等,忙等后面就不能睡眠 */
    	unsigned long busy_end = 0;
    
    	rcu_read_lock();
    	retval = max_select_fd(n, fds);    /* 获取最大文件描述符 */
    	rcu_read_unlock();
    
    	if (retval < 0)
    		return retval;
    	n = retval;
    
    	poll_initwait(&table);            /* 初始化poll_wqueues(见我上节的poll分析) */
    	wait = &table.pt;
    	if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
    		wait->_qproc = NULL;    /* 如果传入时间为0,则非阻塞查询完则退出 */
    		timed_out = 1;
    	}
    
    	if (end_time && !timed_out)
    		slack = select_estimate_accuracy(end_time);    /* 估计需要等待的时间. */
    
    	retval = 0;
    	for (;;) {
    		unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
    		bool can_busy_loop = false;
    
    		inp = fds->in; outp = fds->out; exp = fds->ex;
    		rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
                    /* 遍历所有的描述符, i 文件描述符  */
    		for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
    			unsigned long in, out, ex, all_bits, bit = 1, mask, j;
    			unsigned long res_in = 0, res_out = 0, res_ex = 0;
    
    			in = *inp++; out = *outp++; ex = *exp++;
                        /* 检查当前的fds中一个solt的描述符   */
    			all_bits = in | out | ex;
    			if (all_bits == 0) {
    				i += BITS_PER_LONG;    /* 没有需要监听的描述符,就查看下一个solt */
    				continue;
    			}
                            /* 描述符使用bit表示的solt 表示一个long里有多少个bit(BITS_PER_LONG) */
    
    
    			for (j = 0; j < BITS_PER_LONG; ++j, ++i, bit <<= 1) {
    				struct fd f;
    				if (i >= n)
    					break;
                                    /*  不需要监听描述符 j   */
    				if (!(bit & all_bits))
    					continue;
                                    /* 取得文件结构  */
    				f = fdget(i);
    				if (f.file) {
    					const struct file_operations *f_op;
    					f_op = f.file->f_op;
                                        /* 没有 f_op 的话就认为一直处于就绪状态  */
    					mask = DEFAULT_POLLMASK;
    					if (f_op->poll) {
                                            /* 设置等待事件的掩码  */
    						wait_key_set(wait, in, out,
    							     bit, busy_flag);
                                                /* 获取当前的就绪状态, 并添加到文件的对应等待队列中 */
    						mask = (*f_op->poll)(f.file, wait);
    					}
    					fdput(f);    // 释放文件 对应fdget 
    
                                        /* 检查文件 i 是否已有可读事件就绪 */
    					if ((mask & POLLIN_SET) && (in & bit)) {
    						res_in |= bit;
    						retval++;
    						wait->_qproc = NULL;
    					
                                            /* 检查文件 i 是否已有可写事件就绪 */
    					if ((mask & POLLOUT_SET) && (out & bit)) {
    						res_out |= bit;
    						retval++;
                                        // 如果已有就绪事件就不再向其他文件的  
                                        // 等待队列中添加回调函数 
    						wait->_qproc = NULL;
    					}
                                        /*  检查文件 i 是否已有异常事件  */
    					if ((mask & POLLEX_SET) && (ex & bit)) {
    						res_ex |= bit;
    						retval++;
    						wait->_qproc = NULL;
    					}
    					/* got something, stop busy polling */
    					if (retval) {
    						can_busy_loop = false;
    						busy_flag = 0;
    
    					/*
    					 * only remember a returned
    					 * POLL_BUSY_LOOP if we asked for it
    					 */
    					} else if (busy_flag & mask)
    						can_busy_loop = true;
    
    				}
    			}
    			if (res_in)
    				*rinp = res_in;    
    			if (res_out)
    				*routp = res_out;
    			if (res_ex)
    				*rexp = res_ex;
    			cond_resched();
    		}
    		wait->_qproc = NULL;
                    //监听事件就绪、超时、信号发生  
    		if (retval || timed_out || signal_pending(current))
    			break;
    		if (table.error) {
    			retval = table.error;
    			break;
    		}
    
    		/* only if found POLL_BUSY_LOOP sockets && not out of time */
    		if (can_busy_loop && !need_resched()) {
    			if (!busy_end) {
    				busy_end = busy_loop_end_time();
    				continue;
    			}
    			if (!busy_loop_timeout(busy_end))
    				continue;
    		}
    		busy_flag = 0;
    
    		/*
    		 * If this is the first loop and we have a timeout
    		 * given, then we convert to ktime_t and set the to
    		 * pointer to the expiry value.  转换到内核时间 
    		 */
    		if (end_time && !to) {
    			expire = timespec_to_ktime(*end_time);
    			to = &expire;
    		}
            /* 等待直到超时, 或由回调函数唤醒, 超时后会再次遍历文件描述符(这次不管是否查询到时间,time_out = 1了,都会退出)*/
    		if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
    					   to, slack))
    			timed_out = 1;
    	}
    
        /* 从等待队列中删除table中的等待项,如果驱动的poll函数调用了poll_wait,并且里面动态申请了内存,也会在这里释放 */
    	poll_freewait(&table);
    
    	return retval;
    }

     

    可以看到,select和poll的调用基本是一样的,实现也是类似的。

    其中select最多只能查询1024个文件的状态,而poll没有这个限制

    poll和select的实现基本上是一致的,只是传递参数有所不同,他们的基本流程如下:

    1. 复制用户数据到内核空间

    2. 估计超时时间

    3. 遍历每个文件并调用f_op->poll 取得文件当前就绪状态, 如果前面遍历的文件都没有就绪,向文件插入wait_queue节点

    4. 遍历完成后检查状态:

            a). 如果已经有就绪的文件转到5;

            b). 如果有信号产生,重启poll或select(转到 1或3);

            c). 否则挂起进程等待超时或唤醒,超时或被唤醒后再次遍历所有文件取得每个文件的就绪状态

    5. 将所有文件的就绪状态复制到用户空间

    6. 清理申请的资源

     

    接下来看一下测试程序。

    驱动

    #include <linux/fs.h>       /* 包含file_operation结构体 */
    #include <linux/init.h>     /* 包含module_init module_exit */
    #include <linux/module.h>   /* 包含LICENSE的宏 */
    #include <linux/io.h>
    #include <linux/delay.h>
    #include <linux/wait.h>
    #include <linux/gfp.h>
    #include <linux/interrupt.h>
    #include <linux/device.h>
    #include <linux/gpio.h>
    #include <linux/kernel.h>
    #include <linux/highmem.h> /* For wait_event_interruptible */
    #include <linux/poll.h>
    #include <asm/gpio.h>
    #include <asm/uaccess.h>
    
    
    
    static unsigned int major;
    static struct class *button_class;
    static struct device *button_dev;
    
    static unsigned char key_val;
    /* 定义一个等待队列的头 */
    static DECLARE_WAIT_QUEUE_HEAD(button_wait_q);
    /* 定义一个事件标志 */
    static volatile int ev_press = 0;
    
    
    struct pin_desc {
        unsigned int    pin;
        unsigned int    key_val;
    };
    
    
    /* 按下时 值分别是  0x01 , 0x02 */
    /* 松开时 值分别是  0x00 , 0x00 */
    static struct pin_desc pins_desc[] = {
        {S5PV210_GPH0(2), 0x01},
        {S5PV210_GPH0(3), 0x02},
    };
    
    
    
    static irqreturn_t irq_handler(int irq, void *dev_id)
    {
        struct pin_desc *p = dev_id;
        int pin_val;
    
        pin_val =  gpio_get_value(p->pin);
    
        /* 得到键值,判断时按下还是松开 */
        if(pin_val)
        {
            /* 松开 */
            key_val &= ~p->key_val;
        }
        else
        {
            /* 按下 */
            key_val |= p->key_val;
        }
        /* 标记有事件发生 */
        ev_press = 1;
        /* 唤醒该等待队列 */
        wake_up_interruptible(&button_wait_q);
    
        return IRQ_HANDLED;
    }
    
    
    
    /* open函数  申请中断 */
    static int button_drv_open(struct inode *inode, struct file *file)
    {
        int ret = 0;
    
        ret = request_irq(IRQ_EINT(2), irq_handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "irq-eint2",&pins_desc[0]);
        if(ret)
        {
            printk(KERN_ERR"request_irq IRQ_EINT(2) fail");
        }
        ret = request_irq(IRQ_EINT(3), irq_handler, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "irq-eint3",&pins_desc[1]);
        if(ret)
        {
            printk(KERN_ERR"request_irq IRQ_EINT(3) fail");
        }
    
    
        return 0;
    }
    
    
    static ssize_t button_drv_read(struct file *file, char __user *array, size_t size, loff_t *ppos)
    {
        int len;
    
        if(size < 1)
        {
            return -EINVAL;
        }
    
    
        /* 赋值只是为了消除告警 */
        len = copy_to_user(array , &key_val, 1);
    
        /* 清除中断事件标志  */
        ev_press = 0;
    
        return 1;
    }
    
    
    static unsigned int button_drv_poll(struct file *file, struct poll_table_struct * wait)
    {
        int mask = 0;
    
        /* 将进程挂在button_waitq队列上,不是在这里休眠 */
        poll_wait(file, &button_wait_q, wait);
        if(ev_press)
        {
            mask = POLLIN | POLLRDNORM;   /* 有可读事件 */
            
        }
    
        return mask;
    }
    
    /* 释放中断 */
    static int button_drv_close(struct inode *inode, struct file *file)
    {
        free_irq(IRQ_EINT(2), &pins_desc[0]);
        free_irq(IRQ_EINT(3), &pins_desc[1]);
    
        return 0;
    }
    
    
    static const struct file_operations button_drv_file_operation = {
        .owner      = THIS_MODULE,
        .open       = button_drv_open,
        .read       = button_drv_read,
        .poll       = button_drv_poll,
        .release    = button_drv_close,
    };
    
    
    static int __init button_drv_init(void)
    {
        /* 获取一个自动的主设备号 */
        major =  register_chrdev(0,"button_drv",&button_drv_file_operation);
        if(major < 0)
        {
            printk(KERN_ERR"register_chrdev button_drv fail \n");
            goto err_register_chrdev;
        }
    
        /* 创建一个类 */
        button_class = class_create(THIS_MODULE, "button_class");
        if(!button_class)
        {
            printk(KERN_ERR"class_create button_class fail\n");
            goto err_class_create;
        }
    
        /* 创建从属这个类的设备 */
        button_dev = device_create(button_class,NULL,MKDEV(major, 0), NULL, "button");
        if(!button_dev)
        {
            printk(KERN_ERR"device_create button_dev fail \n");
            goto err_device_create;
        }
    
    
        return 0;
    
    
    /* 倒影式错误处理机制 */
    
    err_device_create:
        class_destroy(button_class);
    err_class_create:
        unregister_chrdev(major,"button_drv");
    err_register_chrdev:
    
        return -EIO;
    }
    
    
    static void __exit button_drv_exit(void)
    {
        
        /* 注销类里面的设备 */
        device_unregister(button_dev);
        /* 注销类 */
        class_destroy(button_class);
        /* 注销字符设备 */
        unregister_chrdev(major,"button_drv");
    }
    
    module_init(button_drv_init);
    module_exit(button_drv_exit);
    MODULE_LICENSE("GPL");

    应用

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <poll.h>
    
    
    int main(int argc,char *argv[])
    {
        char buf[2];
        int ret;
        int maxfd;
        int timecount;
        fd_set rfds, temps;//设置监听读集合
        struct timeval tv;//设置等待时间,0不等待,NULL一直等待。
        
        int fd = open("/dev/button", O_RDWR);
        if(fd < 0)
        {   
            printf("open /dev/%s fail\n",argv[1]);
            return -1; 
        }   
    
        FD_ZERO(&rfds);//清空集合
    
    
        FD_SET(fd, &rfds);//把标准输入句柄0加入到集合中
    
        
        maxfd = fd + 1;
    
        timecount = 0;
        
        while(1)
        {
    
            temps = rfds;
            tv.tv_sec = 5;
            tv.tv_usec = 0;//设置等待时间5s
            /* 查询fd文件的读事件是否准备好,没准备好则休眠5s */
            ret = select(maxfd, &temps, NULL, NULL, &tv);
            
            /* 查询fd文件的读事件是否准备好,没准备好则睡眠,直至准备好 */
            //ret = select(maxfd, &temps, NULL, NULL, NULL);
    
            tv.tv_sec = 0;
            tv.tv_usec = 0;//设置等待时间0s
            /* 查询fd文件的读事件是否准备好,无论是否准备好都立即返回 */
            //ret = select(maxfd, &temps, NULL, NULL, &tv);
            if(ret<0)
            {
                printf("select error, process will eixt\n");
                break;
            }
            else if(0 == ret)
            {
                timecount++;
                printf("\ntime out: %d\n", timecount);
    
            }
            else
            {
                if(FD_ISSET(fd, &rfds))//测试是否有数据
                {
                    read(fd, buf, 1);
                    printf("buf = %d\n", buf[0]);
                }
            }
        }
        close(fd);
        return 0;
    }

     

    可以知道poll和select的功能基本是是一样的。

     

     

    展开全文
  • 驱动中实现file_operations结构的poll方法后,上层应用可以通过select或者poll方法来查询设备是否有数据可以读写。本文记录驱动中实现上层select查询设备是否有数据可以读取的方法。 1、实现file_operation 实现...
  • 转自:... 版权声明:本文为博主原创文章,未经博主允许不得转载。   ...Select - 系统调用 int select - 功能  函数  参数  返回值 Select - 系统调用- 使用...
  • 使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询可对...本系列只关注设备驱动中的函数层,对于应用层具体请参考《APUE》或《TLPI》(linux/unix系统编程手册),楼主手上就有这两本书,在编写应用态测试
  • 关于在驱动程序中编写poll函数的理解 poll函数的实质,是把一个队列加入到poll_table这个大的轮循中,开始轮循,不断的循环检查是否有可以执行的队列,如果有 在file_operations这个结构体中的poll函数 指针是这样...
  • 一、驱动实现select机制的步骤  1、首先初始化一个等待队列头  2、在驱动中实现poll函数,该函数只需做两件事情  a、使用poll_wait()函数将等待队列添加到poll_table中。  b、返回描述设备是否可读或可写的...
  • 本博实时更新《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)的最新进展。 目前已经完成稿件。 2015年8月9日,china-pub开始上线预售: ... 2015年8月20日,各路朋友报喜...
  • 在使用socket或串口的时候应用代码经常使用select来判断有没接收到数据,驱动需要做什么处理,应用层用户才能正确判断有数据收到并读取数据呢?使用select能够监视我们需要监视的文件描述符的变化情况——读写或是...
  • linuxselect函数用法详解 2016-03-09 17:16:43
    select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。   何为文件句柄???? 文件句柄在Linux里很...
  • 阻塞与非阻塞的概念: ...实际驱动中应该把阻塞和非阻塞这种选择权交给应用程序来选择。要实现这个效果 ,就必须让驱动程序知道应用程序的选择。这个信息是通过 file 结构来传递的。 struct f...
  • Linux安装NVIDIA显卡驱动的正确姿势 什么是nouveau驱动? 检测NVIDIA驱动是否成功安装 集显与独显的切换 使用标准仓库进行自动化安装 使用PPA仓库进行自动化安装 使用官方的NVIDIA驱动进行手动安装 Linux...
  • Linux select内核源码剖析 2019-08-22 15:54:26
    select Linux内核源码剖析 select Linux内核源码剖析 select的原理其实是和poll是一样的,都是采用轮询的方式。select相对于poll也许是比较节省空间吧,因为select是采用bitmap来标志的 本文先讲解一下如何在应用层...
  • linux select函数用法 2015-04-03 14:41:57
    select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0...
  • button按键驱动,相对于前面的LED驱动来说。增加了中断服务程序以及等待队列等新知识点。 先上学习的驱动代码。 /********************************************************************************* * ...
  • 写在前面poll机制是基于等待队列wait_queue的,我个人的理解,poll机制是对wait_queue的补充,等待队列会一直等待,直到condition满足条件...poll机制中几个重要的函数设备驱动file_operations中要添加对应的.poll头
  • Linux Select 2018-10-27 13:25:29
    关于select的阻塞4 select总结 select系统调用时用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。 1.函数原型 int select (int ...
  • Linuxselect函数及实例 2017-11-27 15:07:05
    Unix中的函数select和poll用来,支持Unix中I/O复用的功能,在Unix中I/O模型可以分为以一几种: (1)阻塞I/O (2)非阻塞I/O (3)I/O复用(select和poll) (4)信号驱动I/O(SIGIO) (5)异步I/O  ...
  • linux驱动编写(dma驱动 2018-04-09 23:27:51
    联系信箱:feixiaoxing @163.com】 linux下面的驱动虽然什么样的情形都有,但是dma驱动却并不少见。dma可以有很多的好处,其中最重要的功能就是能够帮助我们将数据搬来搬去,这个时候cpu就由时间去做别的事情了,...
  • 【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 Linux下面的声卡驱动很复杂,根本不是一篇...1、linux声卡驱动在哪个目录sound/2、oss、alsa、asoc是什么关系oss是最老的声卡...
  • 【 声明:版权所有,欢迎转载,请勿用于商业用途。...    对于现代嵌入式设备,特别是手机来说,摄像头是很重要的一个...那么,linux是如何支持摄像头的,我们可以来看一下?   1、代码目录地址 drivers/media ...
1 2 3 4 5 ... 20
收藏数 48,939
精华内容 19,575