finally, I got a example, show why we should recreate the epoll after fork.
In this example, the child set a tcp listen, and add it to the epoll set,
but the parent didn't.
when someone try to connet the tcp server, both parent and child wake up.
when parent try to accept the socket, it got an error, because the parent
hasn't set a tcp listener,
but the epoll_wait will always return till the child accept the socket.
this is my example code:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<netdb.h>
#include<fcntl.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<netinet/in.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<arpa/inet.h>
#include<sys/epoll.h>


int StartTCPListener(const char* ip, const char* port)
{
    int server_sockfd;
    socklen_t server_len;
    struct sockaddr_in server_address;
    int flag;
    int reuse = 1;

    server_sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(server_sockfd==-1) return -1;
    setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr(ip);
    server_address.sin_port = htons(atoi(port));
    server_len = sizeof(server_address);
    if( bind(server_sockfd,(struct sockaddr*)&server_address,server_len) )
return -1;
    if( listen(server_sockfd,SOMAXCONN) ) return -1;
    flag = fcntl(server_sockfd,F_GETFL);
    fcntl(server_sockfd,F_SETFL,flag|O_NONBLOCK);
    fcntl(server_sockfd, F_SETFD, FD_CLOEXEC);
    return server_sockfd;
}

int AcceptTCP(int server_fd)
{
    int client_fd;
    socklen_t client_len;
    struct sockaddr_in client_address;
    client_len = sizeof(client_address);
    int flag;

    client_fd = accept(server_fd,(struct sockaddr
*)&client_address,&client_len);
    if(client_fd>0)
    {
        flag = fcntl(client_fd,F_GETFL);
        fcntl(client_fd,F_SETFL,flag|O_NONBLOCK|O_DSYNC);
        fcntl(client_fd, F_SETFD, FD_CLOEXEC);
    }
    return client_fd;
}

static int AddToEpollSet (int efd, int fd, int events)
{
    struct epoll_event e;
    memset(&e, 0, sizeof(e));
    e.events = events;
    e.data.fd = fd;
    if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &e) == -1)
    {
        int err = errno;
        fprintf(stderr, "epoll_ctl(ADD) failed : %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

int main(const int argc,const char* argv[])
{
    int cpid;
    int efd;
    int sfd;
    struct epoll_event listener_evetn;
    int n;
    int cfd;
    const char *msg="Hello world!\n";

    if(argc<2)
    {
        fprintf(stderr,"usage: epoll_test ip port\n");
        return -1;
    }

    efd=epoll_create(8);
    if(efd<0)
    {
        fprintf(stderr,"epoll_create error : %s\n",strerror(errno));
        return -1;
    }
    fcntl(efd, F_SETFD, FD_CLOEXEC);

    cpid=fork();
    if(cpid<0)
    {
        fprintf(stderr,"fork error : %s\n",strerror(errno));
        return -1;
    }
    if(cpid==0)
    {
        sfd = StartTCPListener(argv[1],argv[2]);
        if(sfd<0)
        {
            fprintf(stderr,"set socket listening error :
%s\n",strerror(errno));
            return -1;
        }
        if(AddToEpollSet(efd,sfd,EPOLLIN)<0)
        {
            fprintf(stderr,"add socket to epoll set error :
%s\n",strerror(errno));
            return -1;
        }
    }
    else printf("%d fork %d\n",getpid(),cpid);

    while(1)
    {
        n=epoll_wait(efd,&listener_evetn,1,-1);
        if(n>0)
        {
            if(cpid==0)
            {
                printf("in child progress %d, press Enter to accept the
socket\n",getpid());
                getchar();
            }

            if(listener_evetn.events & EPOLLIN)
            {
                cfd=AcceptTCP(listener_evetn.data.fd);
                if(cfd>0)
                {
                    printf("Accept client\n");
                    write(cfd,msg,strlen(msg));
                    close(cfd);
                    break;
                }
                else
                {
                    fprintf(stderr,"progress %d accept error
%s\n",getpid(),strerror(errno));
                    sleep(1);
                }
            }
        }
    }

    close(sfd);
    close(efd);
}




2016-02-28 14:42 GMT+08:00 adream <[email protected]>:

> Hi,
> The  EVBACKEND_EPOLL part of  libev manual  said:
>            The biggest issue is fork races, however - if
>             a program forks then *both* parent and child process have to
>             recreate the epoll set, which can take considerable time (one
>             syscall per file descriptor) and is of course hard to detect.
>
> I want to know why?
>
> After did some google, this site said, if the child close fds, it will
> lead the fds clear form the epoll set in the parents.
> https://lkml.org/lkml/2007/10/27/25
>
> But my example code show that closing fds in child wouldn't clear the
> parents epoll set.
>
> So I want to know why the parents and child should has to recreate the
> epoll set after fork.
> Thank you.
>
> this is my example code:
>
> #include<stdio.h>
> #include<stdlib.h>
> #include<unistd.h>
> #include<errno.h>
> #include<netdb.h>
> #include<fcntl.h>
> #include<string.h>
> #include<sys/types.h>
> #include<sys/socket.h>
> #include<sys/un.h>
> #include<netinet/in.h>
> #include<netinet/in.h>
> #include<netinet/tcp.h>
> #include<arpa/inet.h>
> #include<sys/epoll.h>
>
>
> int StartTCPListener(const char* ip, const char* port)
> {
>     int server_sockfd;
>     socklen_t server_len;
>     struct sockaddr_in server_address;
>     int flag;
>     int reuse = 1;
>
>     server_sockfd = socket(AF_INET,SOCK_STREAM,0);
>     if(server_sockfd==-1) return -1;
>     setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
>     server_address.sin_family = AF_INET;
>     server_address.sin_addr.s_addr = inet_addr(ip);
>     server_address.sin_port = htons(atoi(port));
>     server_len = sizeof(server_address);
>     if( bind(server_sockfd,(struct sockaddr*)&server_address,server_len) )
> return -1;
>     if( listen(server_sockfd,SOMAXCONN) ) return -1;
>     flag = fcntl(server_sockfd,F_GETFL);
>     fcntl(server_sockfd,F_SETFL,flag|O_NONBLOCK);
>     fcntl(server_sockfd, F_SETFD, FD_CLOEXEC);
>     return server_sockfd;
> }
>
> int AcceptTCP(int server_fd)
> {
>     int client_fd;
>     socklen_t client_len;
>     struct sockaddr_in client_address;
>     client_len = sizeof(client_address);
>     int flag;
>
>     client_fd = accept(server_fd,(struct sockaddr
> *)&client_address,&client_len);
>     if(client_fd>0)
>     {
>         flag = fcntl(client_fd,F_GETFL);
>         fcntl(client_fd,F_SETFL,flag|O_NONBLOCK|O_DSYNC);
>         fcntl(client_fd, F_SETFD, FD_CLOEXEC);
>     }
>     return client_fd;
> }
>
> static int AddToEpollSet (int efd, int fd, int events)
> {
>     struct epoll_event e;
>     memset(&e, 0, sizeof(e));
>     e.events = events;
>     e.data.fd = fd;
>     if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &e) == -1)
>     {
>         int err = errno;
>         fprintf(stderr, "epoll_ctl(ADD) failed : %s\n", strerror(errno));
>         return -1;
>     }
>     return 0;
> }
>
> int main(const int argc,const char* argv[])
> {
>     int cpid;
>     int efd;
>     int sfd = StartTCPListener(argv[1],argv[2]);
>     struct epoll_event listener_evetn;
>     int n;
>     int cfd;
>     const char *msg="Hello world!\n";
>
>     if(sfd<0)
>     {
>         fprintf(stderr,"set socket listening error :
> %s\n",strerror(errno));
>         return -1;
>     }
>     efd=epoll_create(8);
>     if(efd<0)
>     {
>         fprintf(stderr,"epoll_create error : %s\n",strerror(errno));
>         return -1;
>     }
>     if(AddToEpollSet(efd,sfd,EPOLLIN)<0)
>     {
>         fprintf(stderr,"add socket to epoll set error :
> %s\n",strerror(errno));
>         return -1;
>     }
>
>     cpid=fork();
>     if(cpid<0)
>     {
>         fprintf(stderr,"fork error : %s\n",strerror(errno));
>         return -1;
>     }
>     if(cpid==0)
>     {
>         printf("child progress %d\n",getpid());
>         close(sfd);
>         exit(0);
>     }
>     waitpid(cpid,NULL,0);
>     printf("%d fork %d\n",getpid(),cpid);
>     n=epoll_wait(efd,&listener_evetn,1,-1);
>     if(n>0)
>     {
>         if(listener_evetn.events & EPOLLIN)
>         {
>             cfd=AcceptTCP(listener_evetn.data.fd);
>             if(cfd>0)
>             {
>                 write(cfd,msg,strlen(msg));
>                 close(cfd);
>             }
>         }
>     }
>     close(sfd);
>     close(efd);
> }
>
>
>
>
_______________________________________________
libev mailing list
[email protected]
http://lists.schmorp.de/mailman/listinfo/libev

Reply via email to