您的位置 首页 解答

linux里eventfd的原理及使用

linux里eventfd的原理及应用-一般来说:Linux进程间通信有五大方案:管道,消息队列,信号量,共享内存,套接字。

一般来说:Linux进程间通讯有五大计划:管道,音讯行列,信号量,同享内存,套接字。
    管道我不是很熟,只了解一般管道限制与父子进程之间,首要就被我排除了,由于我要做的是彼此独立的进程间通讯,命名管道好像不限制于父子进程,但在内核态怎样运用不清楚。 音讯行列完全不了解。
    信号量的中心是一个内核变量的原子操作,但接口只体现在用户态,并且信号量的P V操作更多做的好像是互斥,而不是我想要的告诉唤醒机制。
    同享内存就更费事了,接口只在用户态,假如自己想做内核态与用户态之间的同享内存,得自己写file,然后供给mmap接口。
    套接字之前仅仅用过af_inet的tcp/udp与af_unix的dgram,仍是上面的那个问题,内核没有明晰的接口供给,尽管能够自己去用比方sock->ops->recvmsg这样的函数去调用,但毕竟需求自己结构入参,感觉仍是不太安全。
    
    那么剩余的好像只要netlink了,这个socket明晰地供给了内核的发包函数,由于它明晰地export出了netlink_kernel_create函数,所以内核态的函数得以用这个sock来进行发包。可是一个是用户态需求注册收包函数,另一个内核态发包仍是免不了要拼装skb,关于我单纯地只想进行告诉唤醒来说仍是过于杂乱了。

    所以我再次寻觅,发现了eventfd这个神器,在KVM与Qemu的通讯之间,eventfd被大牛运用的炉火纯青,细心地剖析了一下源码,发现这个东西就如姓名所说,纯是为了告诉而存在的。
    作为一个file(linux里有不是file的东西么~~),它的private_data结构体 eventfd_ctx只要不幸的四个变量。
struct eventfd_ctx {
    struct kref kref;   /* 这个就不多说了,file计数用的,用于get/put */
    wait_queue_head_t wqh; /* 这个用来寄存用户态的进程wait项,有了它告诉机制才成为可能 */
/*
* Every time that a write(2) is performed on an eventfd, the
* value of the __u64 being written is added to “count” and a
* wakeup is performed on “wqh”. A read(2) will return the “count”
* value to userspace, and will reset “count” to zero. The kernel
* side eventfd_signal() also, adds to the “count” counter and
* issue a wakeup.
*/
    __u64 count;  /* 这个便是一个技能器,应用程序能够自己看着办,read便是取出然后清空,write便是把value加上 */
    unsigned int flags;  /* 一切的file都有的吧,用来寄存堵塞/非堵塞标识或是O_CLOEXEC之类的东西 */
};

我之所以选用它是由于它有 eventfd_signal 这个特别为内核态供给的接口,下面的是注释。

* This funcTIon is supposed to be called by the kernel in paths that do not
 * allow sleeping. In this funcTIon we allow the counter to reach the ULLONG_MAX
 * value, and we signal this as overflow condiTIon by returining a POLLERR  to poll(2).
    其实看代码会更明晰一些

点击(此处)折叠或翻开

int eventfd_signal(struct eventfd_ctx *ctx, int n)

{

unsigned long flags;

if (n < 0)

return -EINVAL;

spin_lock_irqsave(&ctx->wqh.lock, flags);

if (ULLONG_MAX – ctx->count < n)

n = (int) (ULLONG_MAX – ctx->count);

ctx->count += n;

if (waitqueue_active(&ctx->wqh))

wake_up_locked_poll(&ctx->wqh, POLLIN);

spin_unlock_irqrestore(&ctx->wqh.lock, flags);

return n;

}

实质便是做一次唤醒,不必read,也不必write,与eventfd_write的区别是不必堵塞
    
    下面说一下我的详细用法:
    内核态是一个模块,注册一个misc设备,创立内核线程作业(参数为模块的file->private_data)。供给ioctl接口供用户态进程下发自己eventfd创立的fd,保存在内核线程能够访问到的file->private_data中。
    当内核态想告诉用户态时,直接运用eventfd_signal,此刻用户态线程需求先把自己放在eventfd_ctx->wqh上,有两种计划,一个是调用read,一个是调用poll。 假如是read,之后会将eventfd_ctx->count清零,下次还能堵塞住。可是假如运用poll,之后count并未清零,导致再次poll时,即便内核态没有eventfd_signal,poll也会即时回来。
    用户态告诉内核态略微费事一点,,首要需求再创立一个eventfd,然后下发给file->private_data(这儿的操作同上面),额定需求在模块里做一个iotcl,专门担任用户态来告诉内核态,函数里就做eventfd_signal,内核态线程需求先放在eventfd_ctx->wqh上,能够使用vfs_read,或许自己在内核态做一次poll(好像又费事了)。

 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/jieda/90114.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部