看看poll 事件掩码 --- review代码时发现掩码不分的错误

事件	        描述	                                        是否可作为输入(events)	是否可作为输出(revents)
POLLIN	        数据可读(包括普通数据&优先数据)	                是	                    是
POLLOUT	        数据可写(普通数据&优先数据)	                是	                    是
POLLRDNORM	普通数据可读	                                是	                    是
POLLRDBAND	优先级带数据可读(linux不支持)	                是	                    是
POLLPRI	        高优先级数据可读,比如TCP带外数据	                是	                    是
POLLWRNORM	普通数据可写	                                是	                    是
POLLWRBAND	优先级带数据可写	                                是	                     是
POLLRDHUP	TCP连接被对端关闭,或者关闭了写操作,由GNU引入	是	                    是
POLLHUP	        挂起	                                        否	                    是
POLLERR	        错误	                                        否	                    是
POLLNVAL	文件描述符没有打开	                                否	                    是
/*
 *    Wait for a TCP event.
 *
 *    Note that we don't need to lock the socket, as the upper poll layers
 *    take care of normal races (between the test and the event) and we don't
 *    go look at any of the socket buffers directly.
 */
                unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
{
    unsigned int mask;
    struct sock *sk = sock->sk;
    const struct tcp_sock *tp = tcp_sk(sk);
    int state;

    sock_rps_record_flow(sk);

    sock_poll_wait(file, sk_sleep(sk), wait);

    state = sk_state_load(sk);
    if (state == TCP_LISTEN)
        return inet_csk_listen_poll(sk);

    /* Socket is not locked. We are protected from async events
     * by poll logic and correct handling of state changes
     * made by other threads is impossible in any case.
     */

    mask = 0;

    /*
     * POLLHUP is certainly not done right. But poll() doesn't
     * have a notion of HUP in just one direction, and for a
     * socket the read side is more interesting.
     *
     * Some poll() documentation says that POLLHUP is incompatible
     * with the POLLOUT/POLLWR flags, so somebody should check this
     * all. But careful, it tends to be safer to return too many
     * bits than too few, and you can easily break real applications
     * if you don't tell them that something has hung up!
     *
     * Check-me.
     *
     * Check number 1. POLLHUP is _UNMASKABLE_ event (see UNIX98 and
     * our fs/select.c). It means that after we received EOF,
     * poll always returns immediately, making impossible poll() on write()
     * in state CLOSE_WAIT. One solution is evident --- to set POLLHUP
     * if and only if shutdown has been made in both directions.
     * Actually, it is interesting to look how Solaris and DUX
     * solve this dilemma. I would prefer, if POLLHUP were maskable,
     * then we could set it on SND_SHUTDOWN. BTW examples given
     * in Stevens' books assume exactly this behaviour, it explains
     * why POLLHUP is incompatible with POLLOUT.    --ANK
     *
     * NOTE. Check for TCP_CLOSE is added. The goal is to prevent
     * blocking on fresh not-connected or disconnected socket. --ANK
     */
    if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)
        mask |= POLLHUP;// pollHUP的由来   被关闭
    if (sk->sk_shutdown & RCV_SHUTDOWN)
        mask |= POLLIN | POLLRDNORM | POLLRDHUP; //关闭了 读端

    /* Connected or passive Fast Open socket? */
    if (state != TCP_SYN_SENT &&
        (state != TCP_SYN_RECV || tp->fastopen_rsk)) {
        int target = sock_rcvlowat(sk, 0, INT_MAX);

        if (tp->urg_seq == tp->copied_seq &&
            !sock_flag(sk, SOCK_URGINLINE) &&
            tp->urg_data)
            target++;

        if (tp->rcv_nxt - tp->copied_seq >= target)
            mask |= POLLIN | POLLRDNORM;

        if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {//写端没有关闭
            if (sk_stream_is_writeable(sk)) {
                mask |= POLLOUT | POLLWRNORM;
            } else {  /* send SIGIO later */
                sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
                set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

                /* Race breaker. If space is freed after
                 * wspace test but before the flags are set,
                 * IO signal will be lost. Memory barrier
                 * pairs with the input side.
                 */
                smp_mb__after_atomic();
                if (sk_stream_is_writeable(sk))
                    mask |= POLLOUT | POLLWRNORM;
            }
        } else
            mask |= POLLOUT | POLLWRNORM;///写端关闭了

        if (tp->urg_data & TCP_URG_VALID)
            mask |= POLLPRI;//带外数据 oob
    }
    /* This barrier is coupled with smp_wmb() in tcp_reset() */
    smp_rmb();
    if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
        mask |= POLLERR;//出现报错

    return mask;
}

可见:poll 一个socket fd 时 ;对于写只需要关心:POLLOUT, 对于读端需要关心:POLLIN, POLLRDHUP(读端被关闭了),  异常情况下:tcpclose或者SHUTDOWN_MASK了, 需要关注POLLHUP 表示连接被挂起;还要错误事件POLLERR;

epoll:
struct epoll_event {
    uint32_t      events;
    epoll_data_t  data;
};
 revents = event_list[i].events; //取出事件类型
 if (revents & (EPOLLERR|EPOLLHUP)) { //例如对方close掉套接字,这里会感应到
       process
 }
 if (revents & EPOLLRDHUP) {
       读端被关闭了;处理这一次数据
}
 if ((revents & (EPOLLERR|EPOLLHUP))
             && (revents & (EPOLLIN|EPOLLOUT)) == 0) {
   出现错误的同时 也有读写数据;
    则处理一次 读写数据
}


这次上次review新人代码时;发现新人对poll事件掩码不清楚,没有考虑到错误异常的情况,但是错误异常的时候,在水平触发条件下会一直触发唤醒事件导致cpu飙高

原文地址:https://www.cnblogs.com/codestack/p/12817613.html