7-4 select系统调用

该系统调用用于多路监控,当没有一个文件满足要求时2,select将阻塞调用进程。
int select(int maxfd, fd_set *readfds, fd_set*writefds, fd_set* exceptfds, const struct timeval *timeout)
maxfd:文件描述符的范围,比待检测的最大文件描述符数大1
readfds:被读监控的文件描述符集
writefds:被写监控的文件描述符集
exceptfds:被异常监控的文件描述符集
timeout:定时器,0表示立即返回;NULL表示阻塞进程,直到某个文件满足条件;timeout值为正整数,就是等待最长的时间,超过时间将返回。

返回值:
1、正常情况下返回满足要求的文件描述符个数。
2、经过timeout后没有文件fd发生了读写异常事件,将返回0;
3、select被某个信号中断,它将返回-1并设置errno为EINTR;
4、如果出错,返回-1并设置相应的errno。

和select函数结合使用的一些常见宏:
void FD_SET(int fd, fd_set *fdset)将fd添加到文件描述符集fdset中
void FD_CLR(int fd, fd_set *fdset)从fdset集中清除文件描述符fd
void FD_ZREO(fd_set *fdset);清空文件描述符fdset
void FD_ISSET(int fd,fd_set *fdset) 在调用select后,使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。

内核态:
应用程序使用select系统调用实现等待阻塞,而在驱动中使用poll方法实现:
unsigned int(*poll)(struct file *filep, poll_table *wait)
1、使用poll_wait将等待队列添加到poll_table中
2、返回描述符设备是否可读或者可写的掩码:
POLLIN 设备可读
POLLRDNORM 数据可读
POLLOUT 设备可写
POLLWRNORM 数据可写
设备可读返回:POLLIN|POLLRDNORM
设备可写返回:POLLOUT|POLLWRNORM

 

范例:
static unsigned int mem_poll(struct file *filp, poll_table *wait)
{
  struct scull_pipe *dev = filep->private;
  unsigned int mask=0;

  poll_wait(filep, &dev->inq, wait);

  /*返回掩码*/
  if(有数据可读)
  mask=POLLIN|POLLRDNORMAL;

  return mask;
}


select内核调用的实现

static int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
{
    ktime_t expire, *to = NULL;
    struct poll_wqueues table;
    poll_table *wait;
    int retval, i, timed_out = 0;
    u64 slack = 0;
    unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
    unsigned long busy_start = 0;

    rcu_read_lock();
    retval = max_select_fd(n, fds);
    rcu_read_unlock();

    if (retval < 0)
        return retval;
    n = retval;

    poll_initwait(&table);
    wait = &table.pt;
    if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
        wait->_qproc = NULL;
        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;

        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++;
            all_bits = in | out | ex;
            if (all_bits == 0) {
                i += BITS_PER_LONG;
                continue;
            }

            for (j = 0; j < BITS_PER_LONG; ++j, ++i, bit <<= 1) {
                struct fd f;
                if (i >= n)
                    break;
                if (!(bit & all_bits))
                    continue;
                f = fdget(i);
                if (f.file) {
                    const struct file_operations *f_op;
                    f_op = f.file->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);
                    if ((mask & POLLIN_SET) && (in & bit)) {
                        res_in |= bit;
                        retval++;
                        wait->_qproc = NULL;
                    }
                    if ((mask & POLLOUT_SET) && (out & bit)) {
                        res_out |= bit;
                        retval++;
                        wait->_qproc = NULL;
                    }
                    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_start) {
                busy_start = busy_loop_current_time();
                continue;
            }
            if (!busy_loop_timeout(busy_start))
                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 = timespec64_to_ktime(*end_time);
            to = &expire;
        }

        if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
                       to, slack))
            timed_out = 1;
    }

    poll_freewait(&table);

    return retval;
}

 

7-5 自动创建设备文件

1、在之前的字符设备驱动程序过程中,每次都需要直接手动创建字符设备
例如:mknod /dev/char0 c 253 0

2、我去还有内核函数自动创建(2.4b版本存在)
devfd_register(devfd_handle_t dir, const char *name, unsigned int flags, unsigned int major,unsigned int minor, umode_t mode, void *ops, void *info)

3、2.6以后就丢弃了上面这个函数,通过udev代替了devfs
利用udev来实现设备文件的自动创建很简单,在驱动初始化的代码里调用class_create为该设备创建一个class,一般在/sys/class/目录下会有对应的文件;然后再在每个设备调用device_create创建对应的设备。
例子:
struct class *myclass=class_create(THIS_MODULE,"my_device_driver");
device_create(myclass.NULL,MKDEV(major_num,0),NULL,"my_device");
当驱动加载时,udev就会自动在/dev目录下创建my_device设备文件,主设备号是major,次设备号是0