Linux API 之 eventfd

    Linux内核从2.6.26开始加入eventfd。

    由于eventfd是一种文件描述符,所以我们可以使用基于事件(select、poll、epoll)的方式来唤醒线程进行相关操作,这样比signal开销要小、比unixsock速度要快、还能优化锁颗粒。

相关函数:

#头文件    #include <sys/eventfd.h>

#函数原型  int eventfd(unsigned int initval, int flags);

#函数原型  int eventfd_read(int fd, eventfd_t *val);  

#函数原型  int eventfd_write(int fd, eventfd_t val);   


/* 64位的计数器  */
typedef uint64_t eventfd_t;


eventfd_t: 

    其实就是uint64_t;

initval:

    表示eventfd_ctx结构体的count使用初始值;

flags:

    可接受如下3个参数或组合值:

       EFD_CLOEXEC (内核版本2.6.27或以上)
              多进程、关闭相关操作会涉及到,具体未测试。

       EFD_NONBLOCK (内核版本2.6.27或以上)
              将eventfd设置为非阻塞模式,同non-block socket操作方式。

       EFD_SEMAPHORE (内核版本2.6.30或以上)
              如果未设置此标志位,每做一次read后,val可能为所有已写入到event_fd的值的总和;
              如果已设置此标志位,做了多少次write就要进行多少次read;

 

 

进一步,我们还能:

    1、我们在将数据写入到对方线程时,在通知的机制帮助下可以进一步降低锁的颗粒度;

    2、无事件的情况下永久阻塞,这样能减少CPU空转的代码编写;

    3、简化事件的编写难度;

 

由于这里我们只实现一个多线程read、write,我们仅做如下操作:

        1、主线程read、子线程write;

        2、主线程使用epoll等待事件发生;

        3、子线程完成任务后,通过信号让主线程退出。

 

编译命令:

[root@localhost ~]# gcc -o main main.c  -lpthread -std=gnu99

 

main.c(未设置EFD_SEMAPHORE标志位)

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <signal.h>
#include <sys/epoll.h>

pthread_t p,main_p;
int efd;


void work(){
	errno = 0;
	printf("work thread start...\n");
	int number = 1;
	for(int i=1;i<10;i++){
		int size = eventfd_write(efd, (eventfd_t)number);
		printf("i = %d,size = %d,errno = %d\n",i, size,errno);
	}
	printf("work thread end...\n");
	sleep(1);
	pthread_kill(main_p,SIGQUIT);
	return ;
}

int main(int argc, char *argv[]){
	eventfd_t  count;
	int epfd;
	errno = 0;

	struct epoll_event ev,events[5];
	main_p = pthread_self();
	epfd = epoll_create(4);


	efd = eventfd(0,EFD_NONBLOCK);
	//efd = eventfd(0,EFD_NONBLOCK | EFD_SEMAPHORE);
	ev.data.fd = efd;
	ev.events = EPOLLIN ;
	epoll_ctl(epfd,EPOLL_CTL_ADD,efd,&ev);
	pthread_create(&p,NULL,(void*)work,NULL);


	for(;;){
		int len = epoll_wait(epfd,events,5,-1);
		for(int i=0;i<len;i++){
			if(events[i].events == EPOLLIN){
				eventfd_read(efd,&count);
				printf("count = %d\n",count);
			}
		}
	}

	return 0;
}
#返回结果:
[root@localhost ~]# gcc -o main main.c  -lpthread -std=gnu99
[root@localhost ~]# ./main 
work thread start...
i = 1,size = 0,errno = 0
count = 1
count = 1
i = 2,size = 0,errno = 0
i = 3,size = 0,errno = 0
i = 4,size = 0,errno = 0
i = 5,size = 0,errno = 0
i = 6,size = 0,errno = 0
i = 7,size = 0,errno = 0
count = 5
count = 1
i = 8,size = 0,errno = 0
i = 9,size = 0,errno = 0
work thread end...
count = 1
Quit
[root@localhost ~]#

    

 

main.c(已设置EFD_SEMAPHORE标志位)

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <signal.h>
#include <sys/epoll.h>

pthread_t p,main_p;
int efd;


void work(){
	errno = 0;
	printf("work thread start...\n");
	int number = 1;
	for(int i=1;i<10;i++){
		int size = eventfd_write(efd, (eventfd_t)number);
		printf("i = %d,size = %d,errno = %d\n",i, size,errno);
	}
	printf("work thread end...\n");
	sleep(1);
	pthread_kill(main_p,SIGQUIT);
	return ;
}

int main(int argc, char *argv[]){
	eventfd_t  count;
	int epfd;
	errno = 0;

	struct epoll_event ev,events[5];
	main_p = pthread_self();
	epfd = epoll_create(4);


	//efd = eventfd(0,EFD_NONBLOCK);
	efd = eventfd(0,EFD_NONBLOCK | EFD_SEMAPHORE);
	ev.data.fd = efd;
	ev.events = EPOLLIN ;
	epoll_ctl(epfd,EPOLL_CTL_ADD,efd,&ev);
	pthread_create(&p,NULL,(void*)work,NULL);


	for(;;){
		int len = epoll_wait(epfd,events,5,-1);
		for(int i=0;i<len;i++){
			if(events[i].events == EPOLLIN){
				eventfd_read(efd,&count);
				printf("count = %d\n",count);
			}
		}
	}

	return 0;
}
#返回结果
[root@localhost ~]# ./main 
work thread start...
i = 1,size = 0,errno = 0
i = 2,size = 0,errno = 0
i = 3,size = 0,errno = 0
i = 4,size = 0,errno = 0
i = 5,size = 0,errno = 0
i = 6,size = 0,errno = 0
i = 7,size = 0,errno = 0
i = 8,size = 0,errno = 0
i = 9,size = 0,errno = 0
work thread end...
count = 1
count = 1
count = 1
count = 1
count = 1
count = 1
count = 1
count = 1
count = 1
Quit
[root@localhost ~]#

 

未设置EFD_SEMAPHORE标志位时:

        每做一次write,number结果如果未立刻读出将会与下一次write结果进行相加(以此类推);

已设置EFD_SEMAPHORE标志位时:

        每次read将会与queue操作类似(先进先出、且不会相加);

 

由于时间与环境原因,未进行更多测试。如有错误,敬请谅解。

转载于:https://my.oschina.net/CandyMi/blog/1377659

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值