一、mdstat_poll内核函数实现
static const struct seq_operations md_seq_ops = {
.start = md_seq_start,
.next = md_seq_next,
.stop = md_seq_stop,
.show = md_seq_show,
};
static int md_seq_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
int error;
error = seq_open(file, &md_seq_ops);
if (error)
return error;
seq = file->private_data;
// 文件打开时将当前md_event_count的值赋值给poll_event.
// poll_evnet和当前md_event_count计数不一致时事件mask将会被标记为EPOLLERR和EPOLLPRI
seq->poll_event = atomic_read(&md_event_count);
return error;
}
static int md_unloading;
static __poll_t mdstat_poll(struct file *filp, poll_table *wait)
{
struct seq_file *seq = filp->private_data;
__poll_t mask;
// md_exit时会将md_unloading的值置为1
if (md_unloading)
return EPOLLIN|EPOLLRDNORM|EPOLLERR|EPOLLPRI;
// 函数用于将当前进程添加到md_event_waiters等待队列中,以便在有新的RAID事件时被唤醒。
poll_wait(filp, &md_event_waiters, wait);
/* always allow read */
// 可读事件,
mask = EPOLLIN | EPOLLRDNORM;
// 初始poll_event值与当前md_event_count计数不一致。
// 时间类型始终会被打上EPOLLERR | EPOLLPRI标记。
// seq->poll_event在什么时候被赋值?
if (seq->poll_event != atomic_read(&md_event_count))
mask |= EPOLLERR | EPOLLPRI;
return mask;
}
1.1 常见监控的错误类型
- EPOLLIN: 表示文件描述符可读(有数据可读)。当有数据到达,可以进行读取操作时,该事件会被触发。
- EPOLLPRI: 表示有紧急数据可读。这通常用于终端设备,当有“out-of-band”数据时触发。
- EPOLLOUT: 表示文件描述符可写(可以进行写入操作)。当缓冲区有空间,可以写入更多数据时,该事件会被触发。
- EPOLLERR: 表示文件描述符发生了错误。当 I/O 操作因为错误而无法进行时,该事件会被触发。
- EPOLLHUP: 表示挂起(hang up)。当远程端关闭了连接,或者在流式网络连接中检测到一个挂起事件时,该事件会被触发。
- EPOLLNVAL: 表示文件描述符无效。当 epoll_ctl 操作添加了一个不存在的文件描述符到 epoll 实例时,该事件会被触发。
- EPOLLRDNORM: 表示有正常的读取数据。这个事件与 EPOLLIN 类似,但是用于区分不同类型的数据到达。
- EPOLLRDBAND: 表示有带外数据可读。这通常用于检查是否有带外数据到达,例如网络接口的紧急数据。
- EPOLLWRNORM: 表示有正常的写入数据。这个事件与 EPOLLOUT 类似,但是用于区分不同类型的数据写入。
- EPOLLWRBAND: 表示有带外写入数据。这通常用于检查是否有带外数据可以写入。
- EPOLLMSG: 表示有消息队列数据可读。这个事件用于 msgget 系统调用获取的 IPC(Inter-Process Communication)消息队列。
- EPOLLRDHUP: 表示读取时检测到挂起。当读取操作检测到流式网络连接的另一端关闭了连接时,该事件会被触发。
poll_wait函数,什么叫做poll_table?该结构体包含一个函数指针_qproc
和_key
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
if (p && p->_qproc && wait_address)
p->_qproc(filp, wait_address, p); // 调用poll_table注册的函数指针
}
/*
* structures and helpers for f_op->poll implementations
*/
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
/*
* Do not touch the structure directly, use the access functions
* poll_does_not_wait() and poll_requested_events() instead.
*/
typedef struct poll_table_struct {
poll_queue_proc _qproc;
__poll_t _key;
} poll_table;
_qproc
函数指针是在什么时候初始化呢? - - 用户态在调用poll系统调用时被初始化。
|- do_sys_poll()
|- poll_initwait()
|- init_poll_funcptr()
static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
{
pt->_qproc = qproc; // _qroc 被初始化为函数__pollwait
pt->_key = ~(__poll_t)0; /* all events enabled */
}
/*
* Ok, Peter made a complicated, but straightforward mu