第一章基础知识
1.1 pread和pwrite函数
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
pread简单来说就是在指定偏移offset位置开始读取count个字节,同理可推pwrite
1.2 使用示例
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
int fd = open("test.txt", O_RDONLY);
if(fd < 0)
{
perror("open failed");
return 1;
}
char buf[1024] = {0};
int offset = 5;
ssize_t ret = 0;
int count = 10;
if((ret = pread(fd, buf, count, offset)) == -1)
{
perror("pread failed");
return 1;
}
std::cout << "read buf = " << buf << std::endl;
return 0;
}
1.3 devpoll使用
1.初始化
fd = open(“/dev/poll”, flags, (mode_t)mode);获得文件描述符
2.增加事件
struct pollfd *events;
pwrite(fd, events,sizeof(struct pollfd) * eventsnum, 0) 直接往里边写
3.删除事件
使用 POLLREMOVE 事件,然后把这个时间用增加时间的方法写入/dev/poll,系统会自动将我们要删除的描述符时间删除
4.监听事件
struct dvpoll {
struct pollfd *dp_fds;
int dp_nfds;
int dp_timeout;
}
struct dvpoll dvp;
res = ioctl(devpollop->dpfd, DP_POLL, &dvp);返回事件发生的个数
用个for遍历一遍即可
for (i = 0; i < res; i++) {
int which = 0;
int what = dvp.dp_fds[i].revents;
if (what & POLLHUP) {
what |= POLLIN | POLLOUT;
}else if (what & POLLERR) {
what |= POLLIN | POLLOUT;
}
if (what & POLLIN) {
which |= EV_READ;
}
if (what & POLLOUT) {
which |= EV_WRITE;
}
if (!which)
continue;
。。。
}
第二章 devpoll.c分析
int
evutil_open_closeonexec_(const char *pathname, int flags, unsigned mode)
{
int fd;
#ifdef O_CLOEXEC
fd = open(pathname, flags|O_CLOEXEC, (mode_t)mode);
if (fd >= 0 || errno == EINVAL)
return fd;
/* If we got an EINVAL, fall through and try without O_CLOEXEC */
#endif
fd = open(pathname, flags, (mode_t)mode);
if (fd < 0)
return -1;
#if defined(FD_CLOEXEC)
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
close(fd);
return -1;
}
#endif
static void *
devpoll_init(struct event_base *base)
{
int dpfd, nfiles = NEVENT;
struct rlimit rl;
struct devpollop *devpollop;
if (!(devpollop = mm_calloc(1, sizeof(struct devpollop)))) /* 分配指针 */
return (NULL);
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
rl.rlim_cur != RLIM_INFINITY)
nfiles = rl.rlim_cur;
/* Initialize the kernel queue */
if ((dpfd = evutil_open_closeonexec_("/dev/poll", O_RDWR, 0)) == -1) { /* 打开/dev/poll */
event_warn("open: /dev/poll");
mm_free(devpollop);
return (NULL);
}
devpollop->dpfd = dpfd;
/* Initialize fields */
/* FIXME: allocating 'nfiles' worth of space here can be
* expensive and unnecessary. See how epoll.c does it instead. */
devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd));
if (devpollop->events == NULL) {
mm_free(devpollop);
close(dpfd);
return (NULL);
}
devpollop->nevents = nfiles;
devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd));
if (devpollop->changes == NULL) {
mm_free(devpollop->events);
mm_free(devpollop);
close(dpfd);
return (NULL);
}
evsig_init_(base);
return (devpollop);
}
static int
devpoll_commit(struct devpollop *devpollop)
{
/*
* Due to a bug in Solaris, we have to use pwrite with an offset of 0.
* Write is limited to 2GB of data, until it will fail.
*/
if (pwrite(devpollop->dpfd, devpollop->changes, /* 写入changes事件,个数为nchanges */
sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
return (-1);
devpollop->nchanges = 0;
return (0);
}
static int
devpoll_dispatch(struct event_base *base, struct timeval *tv)
{
struct devpollop *devpollop = base->evbase;
struct pollfd *events = devpollop->events;
struct dvpoll dvp;
int i, res, timeout = -1;
if (devpollop->nchanges)
devpoll_commit(devpollop);
if (tv != NULL)
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
dvp.dp_fds = devpollop->events;
dvp.dp_nfds = devpollop->nevents;
dvp.dp_timeout = timeout;
EVBASE_RELEASE_LOCK(base, th_base_lock);
res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (res == -1) {
if (errno != EINTR) {
event_warn("ioctl: DP_POLL");
return (-1);
}
return (0);
}
event_debug(("%s: devpoll_wait reports %d", __func__, res));
for (i = 0; i < res; i++) {
int which = 0;
int what = events[i].revents;
if (what & POLLHUP)
what |= POLLIN | POLLOUT;
else if (what & POLLERR)
what |= POLLIN | POLLOUT;
if (what & POLLIN)
which |= EV_READ;
if (what & POLLOUT)
which |= EV_WRITE;
if (!which)
continue;
/* XXX(niels): not sure if this works for devpoll */
evmap_io_active_(base, events[i].fd, which);
}
return (0);
}
static int
devpoll_queue(struct devpollop *devpollop, int fd, int events) {
struct pollfd *pfd;
if (devpollop->nchanges >= devpollop->nevents) {
/*
* Change buffer is full, must commit it to /dev/poll before
* adding more
*/
if (devpoll_commit(devpollop) != 0)
return (-1);
}
pfd = &devpollop->changes[devpollop->nchanges++];
pfd->fd = fd;
pfd->events = events;
pfd->revents = 0;
return (0);
}
/* 添加一个事件 */
static int
devpoll_add(struct event_base *base, int fd, short old, short events, void *p)
{
struct devpollop *devpollop = base->evbase;
int res;
(void)p;
/*
* It's not necessary to OR the existing read/write events that we
* are currently interested in with the new event we are adding.
* The /dev/poll driver ORs any new events with the existing events
* that it has cached for the fd.
*/
res = 0;
if (events & EV_READ)
res |= POLLIN;
if (events & EV_WRITE)
res |= POLLOUT;
if (devpoll_queue(devpollop, fd, res) != 0)
return (-1);
return (0);
}
/* 删除一个事件 */
static int
devpoll_del(struct event_base *base, int fd, short old, short events, void *p)
{
struct devpollop *devpollop = base->evbase;
int res;
(void)p;
res = 0;
if (events & EV_READ)
res |= POLLIN;
if (events & EV_WRITE)
res |= POLLOUT;
/*
* The only way to remove an fd from the /dev/poll monitored set is
* to use POLLREMOVE by itself. This removes ALL events for the fd
* provided so if we care about two events and are only removing one
* we must re-add the other event after POLLREMOVE.
*/
if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
return (-1);
if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
/*
* We're not deleting all events, so we must resubmit the
* event that we are still interested in if one exists.
*/
if ((res & POLLIN) && (old & EV_WRITE)) {
/* Deleting read, still care about write */
devpoll_queue(devpollop, fd, POLLOUT);
} else if ((res & POLLOUT) && (old & EV_READ)) {
/* Deleting write, still care about read */
devpoll_queue(devpollop, fd, POLLIN);
}
}
return (0);
}