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