对epoll相关知识做简单整理

简介: 对epoll相关知识做简单整理

对epoll相关知识做简单整理:

最近一直在做网络通信相关的开发,对epoll的使用有了一定的认知,就有想法整理一下:

相关内容来自网络,仅供个人笔记

1:什么时候使用epoll

1:通常网络开发(tcp/udp)的时候,使用epoll(IO多路复用)对连接,可读,可写进行监听。

2:epoll事件中可以存储一个指针,这个特性可以用来实现一些复杂的网络处理。

3:用epoll事件中可以存储指针的特性,可以基于此基础上实现一个reactor模型。

2:epoll使用的数据结构和函数

在熟练使用epoll后,终于意识到要从对应的源码试图了解epoll。

这里,获取到对应的头文件<sys/epoll.h>,对epoll做一些认识。

1:标识符 __BEGIN_DECLS和__END_DECLS

#ifdef  __cplusplus
  # define __BEGIN_DECLS  extern "C" {
  # define __END_DECLS  }
  #else
  # define __BEGIN_DECLS
  # define __END_DECLS
  #endif

从源码中可以发现:

__BEGIN_DECLS和__END_DECLS 宏定义其实时 extern “C” { }的重新命名。

这里的作用:

在c和c++混合使用时,C++调用c编译出的相关接口,可以使用这两个宏。

==》简化了extern “C” {} 而已

2:了解相关的结构体,应用层我们所时使用的。

typedef union epoll_data
{
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;
struct epoll_event
{
  uint32_t events;  /* Epoll events */
  epoll_data_t data;  /* User data variable */
} __EPOLL_PACKED;

注意:

  struct epoll_event 是函数参数类型,在整个流程中,通过该结构对已知事件进行获取和处理.

注意epoll_event 结构体中epoll_data_t的定义:

  epoll_data_t 是一个union公用体类型(使用其中的一种类型)

通过这里的事件结构,我们分析我们可以使用到的:

通常:

    1:我们使用epoll_data_t中的int类型保存服务端或者客户端连接的fd。

    2:使用void* 保存我们需要的一些自定义指针(如结构体指针),可以实现一些复杂业务,或者使用这个实现reactor网络事件触发机制。

3:了解对应的一些函数。

1:epoll_create 创建epoll对象

extern int epoll_create (int __size) __THROW;

作用: 创建epoll,返回epoll对象的文件描述符,注意在使用结束要关闭

参数 :__size 在linux 内核版本大于2.6.8 后,这个size 参数就被弃用了,但是传入的值必须大于0。

    ==》早期版本,该参数告诉内核epoll需要的fd文件描述符的个数,使用该大小去申请内存。

返回值:

    成功时返回epoll对象的文件描述符。

    失败时返回-1,并将errno作为错误指示。

简单了解了一下返回-1时,errno的一些设置:

EINVAL : 无效的标志
EMFILE : 用户打开的文件超过了限制
ENFILE : 系统打开的文件超过了限制
ENOMEM : 没有足够的内存完成当前操作

2:epoll_create1 类似epoll_create()

extern int epoll_create1 (int __flags) __THROW;

可以处理一些fork进程时,文件描述符相关问题,通过设置flag:

    可以将flags设置为O_CLOEXEC,参考open函数的O_CLOEXEC标记。

    O_CLOEXEC标记将FD_CLOEXEC常量设置为文件描述符标志,在进程fork时,可以关闭子进程无用文件描述符。(参考

3:epoll_ctl 往epoll对象中操作事件

实现对事件的加入,修改,删除

extern int epoll_ctl (int __epfd, int __op, int __fd,struct epoll_event *__event) __THROW;

相关函数参数:

    __epfd:epoll_create返回值,即epoll对象对应的描述符

    __op:要执行的动作

EPOLL_CTL_ADD:向多路复用实例加入一个连接socket的文件描述符
EPOLL_CTL_MOD:改变多路复用实例中的一个socket的文件描述符的触发事件
EPOLL_CTL_DEL:移除多路复用实例中的一个socket的文件描述符

    __fd: 要操作的文件描述符,如监听服务端fd, 连接进来的客户端fd

    __event:上文中的epoll_event 结构,其中该结构中的events指定了我们关心的事件。

可读,可写,错误,设置触发模式边沿触发还是水平触发
    EPOLLIN:关注文件描述符的可读  ==》即有内容可以读出
    EPOLLOUT:关注文件描述符的可写 ==》即有空间写入内容

返回值:

    成功时返回0,失败时返回-1,相关的errno标志如下:

EBADF : epfd或者fd不是一个有效的文件描述符
EEXIST : op为EPOLL_CTL_ADD,但fd已经被监控
EINVAL : epfd是无效的epoll文件描述符
ENOENT : op为EPOLL_CTL_MOD或者EPOLL_CTL_DEL,并且fd未被监控
ENOMEM : 没有足够的内存完成当前操作
ENOSPC : epoll实例超过了/proc/sys/fs/epoll/max_user_watches中限制的监听数量

源码中关于参数的定义:

/* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */
#define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface.  */
#define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface.  */
#define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure.  */
enum EPOLL_EVENTS
  {
    EPOLLIN = 0x001,          //可读
#define EPOLLIN EPOLLIN
    EPOLLPRI = 0x002,     //发生异常情况,比如所tcp连接中收到了带外消息
#define EPOLLPRI EPOLLPRI
    EPOLLOUT = 0x004,     //可写
#define EPOLLOUT EPOLLOUT
    EPOLLRDNORM = 0x040,      // 有普通数据可读
#define EPOLLRDNORM EPOLLRDNORM 
    EPOLLRDBAND = 0x080,      // 有优先数据可读
#define EPOLLRDBAND EPOLLRDBAND
    EPOLLWRNORM = 0x100,    // 写普通数据不会导致阻塞
#define EPOLLWRNORM EPOLLWRNORM
    EPOLLWRBAND = 0x200,      // 写优先数据不会导致阻塞
#define EPOLLWRBAND EPOLLWRBAND
    EPOLLMSG = 0x400,
#define EPOLLMSG EPOLLMSG
    EPOLLERR = 0x008,      // read/write时出错,对方可能关闭,这个事件是一直监控的
#define EPOLLERR EPOLLERR
    EPOLLHUP = 0x010,        //文件被挂断。这个事件是一直监控的,即使没有明确指定
                //通常表示本端被挂断,一般于RST联系在一起
#define EPOLLHUP EPOLLHUP
    EPOLLRDHUP = 0x2000,     //对端关闭连接或者shutdown写入半连接
#define EPOLLRDHUP EPOLLRDHUP
    EPOLLWAKEUP = 1u << 29,  // 如果EPOLLONESHOT和EPOLLET清除了,并且进程拥有CAP_BLOCK_SUSPEND权限,那么这个标志能够保证事件在挂起或者处理的时候,系统不会挂起或休眠
#define EPOLLWAKEUP EPOLLWAKEUP
    EPOLLONESHOT = 1u << 30,    //一个事件发生并读取后,文件自动不再监控
#define EPOLLONESHOT EPOLLONESHOT
    EPOLLET = 1u << 31  //开启边缘触发,默认的是水平触发,所以我们并未看到EPOLLLT
#define EPOLLET EPOLLET       
  };

4:epoll_wait 对epoll对象中已触发事件的处理

等待epoll实例上已经触发的事件,返回触发事件的数量

extern int epoll_wait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout);

相关参数:

    __epfd: epoll_create函数返回的epoll对象的文件描述符

    __events: 缓冲区,将存储触发的事件

    __maxevents: 要处理的最大事件个数

    __timeout: 超时时间,单位毫秒, -1表示无限等待,0为立即返回

返回值:

    返回就绪事件的数量。

    返回0,表示超时。

    返回-1,根据erron错误码的设置,进行分析:

EBADF : epfd不是一个有效的文件描述符
EFAULT : events指向的内存无权访问
EINTR : 在请求事件发生或者过期之前,调用被信号打断
EINVAL : epfd是无效的epoll文件描述符

注意:

    除了关注本身的可读,可写,以及相关错误,异常情况相关事件,还有一种就是设置触发模式: 水平触发,和边沿触发。

    水平触发和边沿触发时epoll中的另一个知识点,我的另一个博客已经整理~

    水平触发:对于可读,如果缓冲区中有数据,则会一直触发。

        对于可写,如果缓冲区中有空余位置,则会一直触发。

    边沿触发:对于可读,当缓冲区来数据,缓冲区大小发生变化时,会触发一次。

        对于可写,缓冲区大小变化时,会触发一次。 (描述如果不准确,请指正)

4:epoll的实现原理

1:每个epoll对象维持一个独立的eventpoll结构体,管理独立的内存。

2:eventpoll用红黑树存储相关事件,通过epoll_ctl函数实现事件的修改。 ==》高效查找识别

3:加入到的epoll事件会与设备(如网卡)建立回调关系,执行对应的回调函数。

4:回调函数会把触发到的事件加入到eventpoll 中管理的一个双向链表中。

5:每次epoll_wait其实是对这里的双向链表进行检测。

注意:这里的红黑树和链表是公用一个节点的。

5:errno的了解

1:了解到errno的定义:<errno.h>

#ifndef errno
extern int errno;
#endif

使用:

    errno会捕获操作系统最后的错误。

个人理解:

    errno其实是一个全局变量,由操作系统控制,存储操作系统就近发生错误的错误码。

    errno其实就是一个int类型的数字,每个数字对应着相关的错误描述,我们可以用测试的方式打印相关的错误描述:

2:errno的输出:

perror函数:

perror函数errno对应的错误消息的字符串打印到标准错误输出上,即stderr或2上:

#include <stdio.h>
void perror(const char *msg);

该函数会先打印参数中的字符串,再打印errno对应的错误描述。

strerrorr函数:
#include <string.h>
char * strerror(int errno); 
printf("errno is %d [%s] \n", errno, strerror(errno));

传入操作系统的错误描述码,返回该错误码对应的错误描述字符串。

测试:可以试着打印strerror的返回值,传入数字。

相关errno错误描述:

#define EPERM    1  /* Operation not permitted */
#define ENOENT     2  /* No such file or directory */
#define ESRCH    3  /* No such process */
#define EINTR    4  /* Interrupted system call */
#define EIO    5  /* I/O error */
#define ENXIO    6  /* No such device or address */
#define E2BIG    7  /* Argument list too long */
#define ENOEXEC    8  /* Exec format error */
#define EBADF    9  /* Bad file number */
#define ECHILD    10  /* No child processes */
#define EAGAIN    11  /* Try again */
#define ENOMEM    12  /* Out of memory */
#define EACCES    13  /* Permission denied */
#define EFAULT    14  /* Bad address */
#define ENOTBLK   15  /* Block device required */
#define EBUSY   16  /* Device or resource busy */
#define EEXIST    17  /* File exists */
#define EXDEV   18  /* Cross-device link */
#define ENODEV    19  /* No such device */
#define ENOTDIR   20  /* Not a directory */
#define EISDIR    21  /* Is a directory */
#define EINVAL    22  /* Invalid argument */
#define ENFILE    23  /* File table overflow */
#define EMFILE    24  /* Too many open files */
#define ENOTTY    25  /* Not a typewriter */
#define ETXTBSY   26  /* Text file busy */
#define EFBIG   27  /* File too large */
#define ENOSPC    28  /* No space left on device */
#define ESPIPE    29  /* Illegal seek */
#define EROFS   30  /* Read-only file system */
#define EMLINK    31  /* Too many links */
#define EPIPE   32  /* Broken pipe */
#define EDOM    33  /* Math argument out of domain of func */
#define ERANGE    34  /* Math result not representable */
#define ENOSYS    38  /* Invalid system call number */
#define ENOTEMPTY 39  /* Directory not empty */
#define ELOOP   40  /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN  /* Operation would block */
#define ENOMSG    42  /* No message of desired type */
#define EIDRM   43  /* Identifier removed */
#define ECHRNG    44  /* Channel number out of range */
#define EL2NSYNC  45  /* Level 2 not synchronized */
#define EL3HLT    46  /* Level 3 halted */
#define EL3RST    47  /* Level 3 reset */
#define ELNRNG    48  /* Link number out of range */
#define EUNATCH   49  /* Protocol driver not attached */
#define ENOCSI    50  /* No CSI structure available */
#define EL2HLT    51  /* Level 2 halted */
#define EBADE   52  /* Invalid exchange */
#define EBADR   53  /* Invalid request descriptor */
#define EXFULL    54  /* Exchange full */
#define ENOANO    55  /* No anode */
#define EBADRQC   56  /* Invalid request code */
#define EBADSLT   57  /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT    59  /* Bad font file format */
#define ENOSTR    60  /* Device not a stream */
#define ENODATA   61  /* No data available */
#define ETIME   62  /* Timer expired */
#define ENOSR   63  /* Out of streams resources */
#define ENONET    64  /* Machine is not on the network */
#define ENOPKG    65  /* Package not installed */
#define EREMOTE   66  /* Object is remote */
#define ENOLINK   67  /* Link has been severed */
#define EADV    68  /* Advertise error */
#define ESRMNT    69  /* Srmount error */
#define ECOMM   70  /* Communication error on send */
#define EPROTO    71  /* Protocol error */
#define EMULTIHOP 72  /* Multihop attempted */
#define EDOTDOT   73  /* RFS specific error */
#define EBADMSG   74  /* Not a data message */
#define EOVERFLOW 75  /* Value too large for defined data type */
#define ENOTUNIQ  76  /* Name not unique on network */
#define EBADFD    77  /* File descriptor in bad state */
#define EREMCHG   78  /* Remote address changed */
#define ELIBACC   79  /* Can not access a needed shared library */
#define ELIBBAD   80  /* Accessing a corrupted shared library */
#define ELIBSCN   81  /* .lib section in a.out corrupted */
#define ELIBMAX   82  /* Attempting to link in too many shared libraries */
#define ELIBEXEC  83  /* Cannot exec a shared library directly */
#define EILSEQ    84  /* Illegal byte sequence */
#define ERESTART  85  /* Interrupted system call should be restarted */
#define ESTRPIPE  86  /* Streams pipe error */
#define EUSERS    87  /* Too many users */
#define ENOTSOCK  88  /* Socket operation on non-socket */
#define EDESTADDRREQ  89  /* Destination address required */
#define EMSGSIZE  90  /* Message too long */
#define EPROTOTYPE  91  /* Protocol wrong type for socket */
#define ENOPROTOOPT 92  /* Protocol not available */
#define EPROTONOSUPPORT 93  /* Protocol not supported */
#define ESOCKTNOSUPPORT 94  /* Socket type not supported */
#define EOPNOTSUPP  95  /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT  96  /* Protocol family not supported */
#define EAFNOSUPPORT  97  /* Address family not supported by protocol */
#define EADDRINUSE  98  /* Address already in use */
#define EADDRNOTAVAIL 99  /* Cannot assign requested address */
#define ENETDOWN  100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED  103 /* Software caused connection abort */
#define ECONNRESET  104 /* Connection reset by peer */
#define ENOBUFS   105 /* No buffer space available */
#define EISCONN   106 /* Transport endpoint is already connected */
#define ENOTCONN  107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS  109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED  111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH  113 /* No route to host */
#define EALREADY  114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE    116 /* Stale file handle */
#define EUCLEAN   117 /* Structure needs cleaning */
#define ENOTNAM   118 /* Not a XENIX named type file */
#define ENAVAIL   119 /* No XENIX semaphores available */
#define EISNAM    120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT    122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED 125 /* Operation Canceled */
#define ENOKEY    126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED  129 /* Key was rejected by service */
/* for robust mutexes */
#define EOWNERDEAD  130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */
#define ERFKILL   132 /* Operation not possible due to RF-kill */
#define EHWPOISON 133 /* Memory page has hardware error */
目录
相关文章
|
4月前
|
存储 网络协议 安全
Epoll的实现原理
Epoll的实现原理
|
6月前
epoll(2) 使用及源码分析的引子
epoll(2) 使用及源码分析的引子
|
7月前
|
存储 安全 网络协议
epoll的实现原理
epoll的实现原理
80 0
|
7月前
epoll分析
epoll分析
|
7月前
|
存储 Linux
Linux网络编程(epoll函数的使用)
Linux网络编程(epoll函数的使用)
160 0
|
存储 监控 安全
2.9 epoll的实现原理
2.9 epoll的实现原理
104 0
|
存储 网络协议 Java
计网 - Socket 编程:epoll 为什么用红黑树?
计网 - Socket 编程:epoll 为什么用红黑树?
174 0
|
Linux
linux网络编程(六)epoll反应堆
linux网络编程(六)epoll反应堆
193 1
linux网络编程(六)epoll反应堆
|
存储 监控 NoSQL
epoll底层原理
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本。
435 0
epoll底层原理
|
缓存 Linux
linux网络编程(五)epoll进阶
linux网络编程(五)epoll进阶
130 0