eventfd 主要用于实现线程间通讯,可以用于用户态和内核通讯.主要涉及两个系统调用
SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)
{
return do_eventfd(count, flags);
}
SYSCALL_DEFINE1(eventfd, unsigned int, count)
{
return do_eventfd(count, 0);
}
当用户通过这两个系统调用创建eventfd后,这个fd 其实际就是内存中的一个文件,
这点可以从系统调用的实现do_eventfd来看到
static int do_eventfd(unsigned int count, int flags)
{
struct eventfd_ctx *ctx;
int fd;
/* Check the EFD_* constants for consistency. */
BUILD_BUG_ON(EFD_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON(EFD_NONBLOCK != O_NONBLOCK);
if (flags & ~EFD_FLAGS_SET)
return -EINVAL;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
kref_init(&ctx->kref);
init_waitqueue_head(&ctx->wqh);
ctx->count = count;
ctx->flags = flags;
#用户空间得到的就是这个fd,从这里可以看到fd就是内存中的一个问题
#这个文件对应的fops是eventfd_fops
fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx,
O_RDWR | (flags & EFD_SHARED_FCNTL_FLAGS));
if (fd < 0)
eventfd_free_ctx(ctx);
return fd;
}
明白fd是一个内存文件后,就可以通过对这个文件的read和write来通信,其中read和write
都是定义在eventfd_fops 中
static const struct file_operations eventfd_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = eventfd_show_fdinfo,
#endif
.release = eventfd_release,
.poll = eventfd_poll,
.read = eventfd_read,
.write = eventfd_write,
.llseek = noop_llseek,
};
其中read函数如下:
static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct eventfd_ctx *ctx = file->private_data;
ssize_t res;
__u64 ucnt = 0;
DECLARE_WAITQUEUE(wait, current);
if (count < sizeof(ucnt))
return -EINVAL;
spin_lock_irq(&ctx->wqh.lock);
res = -EAGAIN;
if (likely(res > 0)) {
#调用eventfd_ctx_do_read来对eventfd_ctx中的成员变量count 减掉cnt
eventfd_ctx_do_read(ctx, &ucnt);
if (waitqueue_active(&ctx->wqh))
wake_up_locked_poll(&ctx->wqh, EPOLLOUT);
}
spin_unlock_irq(&ctx->wqh.lock);
if (res > 0 && put_user(ucnt, (__u64 __user *)buf))
return -EFAULT;
return res;
}
static void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt)
{
*cnt = (ctx->flags & EFD_SEMAPHORE) ? 1 : ctx->count;
ctx->count -= *cnt;
}
可见eventfd就对应内存中的文件,对这个文件read的话,则对计数器加,对这个文件写的话,
则对计数器减,以此来最为线程间通讯机制.
函数 anon_inode_getfd(),该函数的作用类似于sock_map_fd(),就是将eventpoll实例映射到一个文件中:
int anon_inode_getfd(const char *name, const struct file_operations *fops,
void *priv, int flags)
{
int error, fd;
struct file *file;
/*
* 分配一个空闲的文件描述符。
*/
error = get_unused_fd_flags(flags);
if (error < 0)
return error;
fd = error;
file = anon_inode_getfile(name, fops, priv, flags);
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto err_put_unused_fd;
}
fd_install(fd, file);
return fd;
err_put_unused_fd:
put_unused_fd(fd);
return error;
}
该函数首先调用get_unused_fd_flags()分配一个空闲的文件描述符,然后创建一个匿名文件,附加上去。因为涉及到文件系统的操作,不做过多的分析。
转载于:http://blog.chinaunix.net/uid-28443939-id-3470593.html
epoll源码分析—sys_epoll_create()函数
find_next_zero_bit()函数
anon_inode_getfile()是一个宏定义,对应的函数时alloc_fd(),alloc_fd()中调用find_next_zero_bit()在文件描述符的位图中查找一个空闲的bit位,空闲的bit位的索引即为找到的文件描述符,我对这个函数比较感兴趣,特别研究一个一番,跟大家分享一下。
/*
* find_next_zero_bit返回的值的范围是0~(size-1),相当于是bit数组中的索引
* @addr: 位图的地址
* @size: 位图的bit位个数
* @offset: 可以理解为bit数组中的索引,也就是说从这个bit位开始查找
*/
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
/*
* 这里BITOP_WORD用来计算offset对应位图中的unsigned long元素在
* addr数组中的索引,所以p为offset所在的unsigned long元素的地址
*/
const unsigned long *p = addr + BITOP_WORD(offset);
/*
* 相当于是offset - (offset % BITS_PER_LONG),也就是offset所在的unsigned long
* 之前所有unsigned long元素的bit位个数
*/
unsigned long result = offset & ~(BITS_PER_LONG-1);
unsigned long tmp;
/*
* 如果偏移量大于等于位图的大小,则直接
* 返回size。
*/
if (offset >= size)
return size;
/*
* 计算offset所在的unsigned long及其之后所有的unsigned long元素中
* 的bit位个数
*/
size -= result;
/*
* 计算offset对应的unsigned long中占用的bit位所在的位置,这个也可以理解
* 为一个索引。假设计算前offset的值为67,计算后offset的值为3,也就是所在
* unsigned long中的第4个bit位。
*/
offset %= BITS_PER_LONG;
if (offset) {
/*
* tmp的值为offset所在的unsigned long的值
*/
tmp = *(p++);
/*
* BITS_PER_LONG - offset计算的offset所在的unsigned long元素中offset所在的
* bit位及其之后的bit位个数。tmp中offset所对应的bit位及其之后的bit位都保留,而将
* tmp中offset所对应的bit位之前的bit位都设置为1.
*/
tmp |= ~0UL >> (BITS_PER_LONG - offset);
/*
* 如果size小于BITS_PER_LONG,说明offset在最后一个unsigned long元素。
*/
if (size < BITS_PER_LONG)
goto found_first;
/*
* 如果tmp取反后为不为0,则说明tmp中有为0的bit位,因此从
* tmp中查找空闲的bit位。
*/
if (~tmp)
goto found_middle;
/*
* 如果offset所对应的bit位所在的unsigned long中没有空闲的bit位,
* 开始从其之后得unsigned long元素中查找。计算剩余的bit位个数,
* 修改已经查找的bit位个数
*/
size -= BITS_PER_LONG;
result += BITS_PER_LONG;
}
/*
* 如果size小于BITS_PER_LONG,则退出循环
*/
while (size & ~(BITS_PER_LONG-1)) {
/*
* 将下一个查找的元素存储在tmp中,如果tmp取反后不为0,则说明tmp中有为0的bit位,因此
* 从tmp中查找空闲的bit位。
*/
if (~(tmp = *(p++)))
goto found_middle;
/*
* 计算剩余的bit位个数,修改已经查找的bit位个数
*/
result += BITS_PER_LONG;
size -= BITS_PER_LONG;
}
/*
* 如果全部查找后,仍没有找到空闲的bit位,则直接返回result。
* 此时result的值应该为位图的bit位的个数。
*/
if (!size)
return result;
/*
* 如果size不为0,则在剩余的最后的bit位(剩余的个数小于BITS_PER_LONG)中查找。
*/
tmp = *p;
found_first:
/*
* 因为剩余的bit位个数有可能小于BITS_PER_LONG,因此需要将unsigned long中
* 不用的bit位置为1,以免干扰后续的查找
*/
tmp |= ~0UL << size;
/*
* 如果所有bit位都为1,则说明没有空余的bit位,
* 则返回所有的bit位的个数。
*/
if (tmp == ~0UL) /* Are any bits zero? */
return result + size; /* Nope. */
found_middle:
/*
* ffz(tmp)返回的是tmp中第一个为0的bit位的索引
*/
return result + ffz(tmp);
}