epoll源代码分析

这篇博客详细分析了Linux内核v2.6.26.8版本中的epoll机制,包括epoll_filefd、list_head、epoll_event等核心结构,以及红黑树节点、epitem和eventpoll的数据结构。还探讨了在epoll_create、epoll_ctl、epoll_wait等关键系统调用中的操作,如ep_alloc内存分配,epoll_fops文件操作和ep_poll回调处理。文章特别指出在epoll事件传输过程中,锁的使用策略,包括自旋锁、互斥锁以及全局互斥锁的作用和获取场景。
摘要由CSDN通过智能技术生成

linux内核代码版本:v2.6.26.8

当系统启动时,epoll会进行初始化操作:

//用于实现安全的poll唤醒,避免在wake_up()内部再次进入poll回调
struct poll_safewake {
   
	struct list_head wake_task_list;
	spinlock_t lock;
};

//用来序列化ep_free()和eventpoll_release_file()的互斥量
static struct mutex epmutex;

/* Safe wake up implementation */
static struct poll_safewake psw;

static int __init eventpoll_init(void)
{
   
	mutex_init(&epmutex);		//初始化互斥量

	/* Initialize the structure used to perform safe poll wait head wake ups */
	ep_poll_safewake_init(&psw);		

	//slab动态分配内存,用于分配struct epitm
	epi_cache = kmem_cache_create("eventpoll_epi", sizeof(struct epitem),
			0, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC,
			NULL);			

	 //slab动态分配内存,用于分配struct eppoll_entry
	pwq_cache = kmem_cache_create("eventpoll_pwq",
			sizeof(struct eppoll_entry), 0,
			EPI_SLAB_DEBUG|SLAB_PANIC, NULL);	
	
    return 0;
}
fs_initcall(eventpoll_init);

epoll需要三个级别的锁。

1.epmutex(mutex);
2.ep->mtx(mutex);
3.ep->lock(spinlock);

对于自旋锁ep->lock(spinlock),因为我们在poll回调内部操作对象,该回调可能是由wake_up触发的,而wake_up有可能从中断请求上下文调用。所以我们不能在poll回调中休眠,因此我们需要一个spinlock

在事件传输循环(从内核到用户空间)期间,由于copy_to_user(),我们可能需要一个允许我们进入睡眠状态的锁。这个锁是一个互斥锁。epoll_ctl(EPOLL_CTL_DEL)eventpoll_release_file()期间获取的。然后我们还需要一个全局互斥锁来序列化eventpoll_release_file()ep_free()

这个互斥锁是在epoll文件清理路径期间由ep_free()获取的,如果文件已被pushepoll set中,则它也由eventpoll_release_file()获取,然后在没有之前调用epoll_ctl(EPOLL_CTL_DEL)的情况下关闭它。

epoll_filefd

struct epoll_filefd {
   
	struct file *file;
	int fd;
};

list_head

struct list_head {
   
	struct list_head *next, *prev;
};

epoll_event

struct epoll_event {
   
	__u32 events;
	__u64 data;
} EPOLL_PACKED;

红黑树的节点

struct rb_root
{
   
	struct rb_node *rb_node;
};

struct rb_node
{
   
	unsigned long  rb_parent_color;		//父节点颜色
#define	RB_RED		0
#define	RB_BLACK	1
	struct rb_node *rb_right;			//左子树
	struct rb_node *rb_left;			//右子树
} __attribute__((aligned(sizeof(long))));

在这里插入图片描述

epitem

//添加到eventpoll的每一个文件描述符都会有一个链接到红黑树的epitem条目
struct epitem {
   
    //红黑树节点用于将epitm链接到eventpoll红黑树
	struct rb_node rbn;

    //用于将epitem链接到eventpoll就绪列表的列表头
	struct list_head rdllink;

	/*
	 * Works together "struct eventpoll"->ovflist in keeping the
	 * single linked chain of items.
	 */
	struct epitem *next;

	//文件描述符的信息
	struct epoll_filefd ffd;

    //附加在poll操作的活动等待队列数
	int nwait;

	//包含poll wait queue列表
	struct list_head pwqlist;

    //包含epitem的容器
	struct eventpoll *ep;

	//将epitem链接到“struct file”项列表的列表头
	struct list_head fllink;

	//描述感兴趣事件的结构和源文件描述符的结构
	struct epoll_event event;
};

在这里插入图片描述

eventpoll

//该结构存储在文件结构的“private_data”成员中,并代表eventpoll接口的主要数据结构
struct eventpoll {
   
	//保护这个结构
	spinlock_t lock;

	//该互斥锁用于确保epoll使用文件时不会删除这些文件
    //在事件收集循环,文件清理路径,epoll文件退出代码和ctl操作时持有
	struct mutex mtx;

	//sys_epoll_wait()使用的等待队列
	wait_queue_head_t wq;

	//file->poll()使用的等待队列
	wait_queue_head_t poll_wait;

	//就绪文件描述符列表
	struct list_head rdllist;

	//红黑树树根用于存储受监控的fd结构
	struct rb_root rbr;

	//这是一个单链表,它链接了在将就绪事件传输到用户空间时发生的所有“epitem”
    //不需要持有锁
	struct epitem *ovflist;
};

在这里插入图片描述

eppoll_entry

/* poll hooks使用的等待结构 */
struct eppoll_entry {
   
	/* 将eppoll_entry链接到“struct epitem”的队列头 */
	struct list_head llink;

	/* base指针指向"struct epitem"的容器 */
	void *base;

	/*
	 * wait queue项链接到目标文件等待队列头
	 */
	wait_queue_t wait;

	/* 链接等待队列项的等待队列头 */
	wait_queue_head_t *whead;
};

在这里插入图片描述

在这里插入图片描述

sys_epoll_create(int size)

/*
 * 打开一个eventepoll文件描述符。size参数现在已经被抛弃
 */
asmlinkage long sys_epoll_create(int size)
{
   
	int error, fd = -1;
	struct eventpoll *ep;

	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n",
		     current, size));

	/*
	 * 对size参数进行完整性检查
	 */
	error = -EINVAL;			//EINVAL表示无效的参数
	if (size <= 0 || (error = ep_alloc(&ep)) < 0) {
    	//ep_alloc初始化eventpoll结构
		fd = error;
		goto error_return;
	}

	/*
	 * 创建设置eventpoll文件所需的所有项目。即,文件结构和空闲文件描述符
	 * 将创建的eventpoll文件和文件描述符关联起来
	 * static const struct file_operations eventpoll_fops = {
	 *		.release	= ep_eventpoll_release,
	 *		.poll		= ep_eventpoll_poll
	 *  };
	 */
	fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep);
	if (fd < 0)
		ep_free(ep);

error_return:
	DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n",
		     current, size, fd));

	return fd;
}

ep_alloc

static int ep_alloc(struct eventpoll **pep)
{
   
	struct 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值