libevent库源码学习-evport(Event ports)(Solaris 10)

本文介绍了Solaris 10引入的Event Ports机制,包括port_create()、port_associate()、port_get()和port_getn()等API的使用,并通过示例展示了如何在事件驱动编程中利用这些接口处理多线程并发事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Solaris 10 的新增加的特性。
一.API
1. port_create()
    原型:int port_create(void);
    
    port_create() 创建一个 Event ports 队列,返回一个文件描述符作为该
    Event port 的代表。

    相似:kqueue(),epoll_create()

2. port_associate()
    原型:int port_associate(int port, int source,  uintptr_t  object,
     int events, void *user);

    port_associate() 将某一个对象的特定 event 与 Event port 相关联。当
    source 为 PORT_SOURCE_FD 时,object 就是文件描述符。events 可以参考
    poll(2) 的。user 是一个用户自定义的指针,与该 object 相关的。在
    kqueue(2) 和 epoll(4) 也提供了类似的用户自定义指针。在前面的 echo
    server 例子中,传入了一个函数指针给 user,这样在 main() 的事件处理
    主循环中,代码就可以写得非常简洁。

    需要注意的是,当用 port_get() 取得某个 object 的 event 之后,这个
    object 与 port 也就不再相关联了。如果想继续取得这个 object 的 event,
    必须再次调用 port_associate() 将 object 与 port 关联。这种设计显然
    是为多线程程序而做的,当某一个线程取得一个 event 之后,object 就从
    port 的对列中删掉了,这样可以保证这个线程完完整整地处理完这个 event,
    不用担心别的线程也会取得这个 event。

    相似:kevent(),epoll_ctl()

3. port_get()
    原型:int port_get(int port, port_event_t  *pe,  const  timespec_t
     *timeout);

    port_get() 每次从 port 中取回一个 event。如果 timeout 参数为 NULL,
    port_get() 会一直等待,直到某一个 event 到来。pe 用于存储返回的
    event。

    相似:kevent(),epoll_wait()

4. port_getn()
    原型:int port_getn(int port,  port_event_t  list[],  uint_t  max,
     uint_t *nget, const timespec_t *timeout);

    port_getn() 与 port_get() 都是用于从 port 中取回 event,不同的是
    port_getn() 可以一次取回多个。list 数组用于存储返回的多个 events,
    max 为 list 数组的元素个数。timeout 与 port_get() 的一致。
    
    需要特别注意的是 nget 参数,这是一个 value-result 参数,也就是传入
    n,告诉内核要取得 n 个 event,当 port_getn() 返回时,getn 的值表示
    内核实际取得了多少个。当 timeout 为 NULL 时,port_getn() 会一直等待,
    直到确实取得了 n 个 event 之后才会返回,这一点是与 kevent() 和
    epoll_wait() 很不相同的地方。如果 timeout 不为 NULL,则只要超时就返
    回,不管是不是已经取到了 n 个 event。(注:这里忽略了其他可能引起
    port_getn() 返回的因素) 这也是前面的 echo server 代码中要设置一个
    50 毫秒超时的原因。大家可以试试把 NULL 传给 timeout 的效果。

    相似:kevent(),epoll_wait()

二.例子

str_srv.c:
/* 用 Event ports 实现的 UNPv1 ECHO Server 例子程序。
 *
 * 编译命令:
 *   gcc -o str_srv str_srv.c -lsocket
 *
 *                   flyriver 2004.11.02
 */
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/poll.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <port.h> /* for event ports */

#define SERV_PORT 9877
#define MAXLINE 80

#ifndef OPEN_MAX
#define OPEN_MAX 256
#endif

typedef void (*port_func_t)(port_event_t *);

int portfd;
uint_t maxi;

static void echo(port_event_t *ev)
{
    ssize_t n;
    int sockfd;
    char buf[MAXLINE];

    sockfd = ev->portev_object;
    if (ev->portev_events & POLLIN)
    {
        if ((n = read(sockfd, buf, MAXLINE)) < 0)
        {
            perror("read");
            close(sockfd);
            maxi--;
        }
        else if (n == 0) /* connection closed by client */
        {
            close(sockfd);
            maxi--;
        }
        else
        {
            write(sockfd, buf, n);
            /* 重新关联 client fd 到 event ports */
            port_associate(portfd, PORT_SOURCE_FD, sockfd, POLLIN, echo);
        }
    }
}

static void new_conn(port_event_t *ev)
{
    int connfd;
    socklen_t clilen;
    struct sockaddr_in cliaddr;
    int listenfd;

    listenfd = ev->portev_object;
    clilen = sizeof(cliaddr);
    connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);

    /* 关联 client fd 到 event ports */
    port_associate(portfd, PORT_SOURCE_FD, connfd, POLLIN, echo);
    maxi++;

    /* 重新关联 listenfd 到 event ports */
    port_associate(portfd, PORT_SOURCE_FD, listenfd, POLLIN, new_conn);
}

int main()
{
    int listenfd;
    struct sockaddr_in servaddr;
    int optval;
    /* variables for event ports version */
    int i;
    uint_t nready;
    port_event_t client[OPEN_MAX];
    timespec_t timeout;
    port_func_t port_func;

    bzero(&servaddr, sizeof(servaddr));
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, 4);
    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, 5);

    /* 创建 event ports */
    portfd = port_create();
    /* 关联 listenfd 到 event ports */
    port_associate(portfd, PORT_SOURCE_FD, listenfd, POLLIN, new_conn);
    maxi++;
    timeout.tv_sec = 0;
    timeout.tv_nsec = 50000000; /* 50 毫秒 */
    for (;;)
    {
        /* get events here*/
        nready = maxi;
        if (port_getn(portfd, client, OPEN_MAX, &nready, &timeout) < 0
                && errno != ETIME)
        {
            if (errno == EINTR)
                continue;
            else
            {
                perror("port_getn");
                return -1;
            }
        }
        for (i = 0; i < nready; i++)
        {
            if (client[i].portev_source == PORT_SOURCE_FD)
            {
                port_func = (port_func_t)client[i].portev_user;
                port_func(&client[i]);
            }
        }
    }
    close(portfd);

    return 0;
}


参考:http://www.smth.edu.cn/bbsgcon.php?board=OS&file=T/G.1099404898.p0&num=526
http://bbs.tsinghua.edu.cn/bbsgcon.php?board=OS&file=j/G.1099375601.80&num=525

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值