博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多进程服务器中,epoll的创建应该在创建子进程之后
阅读量:5303 次
发布时间:2019-06-14

本文共 5042 字,大约阅读时间需要 16 分钟。

看我的测试代码,似乎应该是在创建子进程之后创建epoll的fd,否则程序将会有问题,试将代码中两个CreateWorker函数的调用位置分别调用,一个在创建epoll fd之前,一个在之后,在调用在创建之前的代码会出问题,在我的机器上(linux内核2.6.26)表现的症状就是所有进程的epoll_wait函数返回0, 而客户端似乎被阻塞了:

服务器端:

#include 
<
iostream
>
#include 
<
sys
/
socket.h
>
#include 
<
sys
/
epoll.h
>
#include 
<
netinet
/
in
.h
>
#include 
<
arpa
/
inet.h
>
#include 
<
fcntl.h
>
#include 
<
unistd.h
>
#include 
<
stdio.h
>
#include 
<
errno.h
>
#include 
<
sys
/
types.h
>
#include 
<
sys
/
wait.h
>
using
 
namespace
 std;
#define
 MAXLINE 5
#define
 OPEN_MAX 100
#define
 LISTENQ 20
#define
 SERV_PORT 5000
#define
 INFTIM 1000
typedef 
struct
 task_t
{
    
int
 fd;
    
char
 buffer[
100
];
    
int
 n;
}task_t;
int
 CreateWorker(
int
 nWorker)
{
    
if
 (
0
 
<
 nWorker)
    {
        
bool
 bIsChild;
        pid_t nPid;
        
while
 (
!
bIsChild)
        {
            
if
 (
0
 
<
 nWorker)
            {
                nPid 
=
 ::fork();
                
if
 (nPid 
>
 
0
)
                {
                    bIsChild 
=
 
false
;
                    
--
nWorker;
                }
                
else
 
if
 (
0
 
==
 nPid)
                {
                    bIsChild 
=
 
true
;
                    printf(
"
create worker %d success!\n
"
, ::getpid());
                }
                
else
                {
                    printf(
"
fork error: %s\n
"
, ::strerror(errno));
                    
return
 
-
1
;
                }
            }
            
else
 
            {
                
int
 nStatus;
                
if
 (
-
1
 
==
 ::wait(
&
nStatus))
                {
                    
++
nWorker;
                }
            }
        }
    }
    
return
 
0
;
}
void
 setnonblocking(
int
 sock)
{
    
int
 opts;
    opts
=
fcntl(sock,F_GETFL);
    
if
(opts
<
0
)
    {
        perror(
"
fcntl(sock,GETFL)
"
);
        exit(
1
);
    }
    opts 
=
 opts
|
O_NONBLOCK;
    
if
(fcntl(sock,F_SETFL,opts)
<
0
)
    {
        perror(
"
fcntl(sock,SETFL,opts)
"
);
        exit(
1
);
    }   
}
int
 main()
{
    
int
 i, maxi, listenfd, connfd, sockfd,epfd,nfds;
    ssize_t n;
    
char
 line[MAXLINE];
    socklen_t clilen;
    
struct
 epoll_event ev,events[
20
];
    
struct
 sockaddr_in clientaddr;
    
struct
 sockaddr_in serveraddr;
    listenfd 
=
 socket(AF_INET, SOCK_STREAM, 
0
);
       bzero(
&
serveraddr, 
sizeof
(serveraddr));
    serveraddr.sin_family 
=
 AF_INET;
    
char
 
*
local_addr
=
"
127.0.0.1
"
;
    inet_aton(local_addr,
&
(serveraddr.sin_addr));
//
htons(SERV_PORT);
    serveraddr.sin_port
=
htons(SERV_PORT);
      
//
 地址重用
    
int
 nOptVal 
=
 
1
;
    socklen_t nOptLen 
=
 
sizeof
(
int
);
    
if
 (
-
1
 
==
 ::setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 
&
nOptVal, nOptLen))
    {
        
return
 
-
1
;
    }    
    setnonblocking(listenfd);
    bind(listenfd,(sockaddr 
*
)
&
serveraddr, 
sizeof
(serveraddr));
    listen(listenfd, LISTENQ);    
    
    CreateWorker(
5
);
    
    
//
把socket设置为非阻塞方式
    
    
//
生成用于处理accept的epoll专用的文件描述符
    epfd
=
epoll_create(
256
);    
    
//
设置与要处理的事件相关的文件描述符
    ev.data.fd
=
listenfd;
    
//
设置要处理的事件类型
    ev.events
=
EPOLLIN
|
EPOLLET;
    
//
ev.events=EPOLLIN;
    
//
注册epoll事件
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,
&
ev);
 
     
//
CreateWorker(5);
     
    maxi 
=
 
0
;
    
    task_t task; 
    task_t 
*
ptask;
    
while
(
true
    {
        
//
等待epoll事件的发生
        nfds
=
epoll_wait(epfd,events,
20
,
500
);
        
//
处理所发生的所有事件     
        
for
(i
=
0
;i
<
nfds;
++
i)
        {
            
if
(events[i].data.fd
==
listenfd)
            {                
                connfd 
=
 accept(listenfd,NULL, NULL);
                
if
(connfd
<
0
){                    
                    printf(
"
connfd<0, listenfd = %d\n
"
, listenfd);
                    printf(
"
error = %s\n
"
, strerror(errno));
                    exit(
1
);
                }
                setnonblocking(connfd);
               
                
//
设置用于读操作的文件描述符
                memset(
&
task, 
0
sizeof
(task));
                task.fd 
=
 connfd;
                ev.data.ptr 
=
 
&
task;
                
//
设置用于注册的读操作事件
                ev.events
=
EPOLLIN
|
EPOLLET;
                
//
ev.events=EPOLLIN;
                
//
注册ev
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,
&
ev);
            }
            
else
 
if
(events[i].events
&
EPOLLIN)
            {
                cout 
<<
 
"
EPOLLIN
"
 
<<
 endl;
                ptask 
=
 (task_t
*
)events[i].data.ptr;
                sockfd 
=
 ptask
->
fd;
                
                
if
 ( (ptask
->
=
 read(sockfd, ptask
->
buffer, 
100
)) 
<
 
0
) {
                    
if
 (errno 
==
 ECONNRESET) {
                        close(sockfd);
                        events[i].data.ptr 
=
 NULL;
                    } 
else
                        std::cout
<<
"
readline error
"
<<
std::endl;
                } 
else
 
if
 (ptask
->
==
 
0
) {
                    close(sockfd);
                    events[i].data.ptr 
=
 NULL;
                }
                ptask
->
buffer[ptask
->
n] 
=
 
'
\0
'
;
                cout 
<<
 
"
read 
"
 
<<
 ptask
->
buffer 
<<
 endl;
                
                
//
设置用于写操作的文件描述符                                
                ev.data.ptr 
=
 ptask;
                
//
设置用于注测的写操作事件
                ev.events
=
EPOLLOUT
|
EPOLLET;
                                
                
//
修改sockfd上要处理的事件为EPOLLOUT
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,
&
ev);
            }
            
else
 
if
(events[i].events
&
EPOLLOUT)
            {   
                cout 
<<
 
"
EPOLLOUT
"
 
<<
 endl;
                ptask 
=
 (task_t
*
)events[i].data.ptr;
                sockfd 
=
 ptask
->
fd;
                
                write(sockfd, ptask
->
buffer, ptask
->
n);
                
                
//
设置用于读操作的文件描述符              
                ev.data.ptr 
=
 ptask;
                
                
//
修改sockfd上要处理的事件为EPOLIN
                epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,
&
ev);
                cout 
<<
 
"
write 
"
 
<<
 ptask
->
buffer;
                memset(ptask, 
0
sizeof
(
*
ptask));
                close(sockfd);
            }
        }
    }
    
return
 
0
;
}
测试客户端:
#
!/
usr
/
bin
/
perl
use strict;
use Socket;
use IO::Handle;
sub echoclient
{
    my $host 
=
 
"
127.0.0.1
"
;
    my $port 
=
 
5000
;
    my $protocol 
=
 getprotobyname(
"
TCP
"
);
    $host 
=
 inet_aton($host);
    socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die 
"
socket() failed: $!
"
;
    my $dest_addr 
=
 sockaddr_in($port, $host);
    connect(SOCK, $dest_addr) or die 
"
connect() failed: $!
"
;
    SOCK
->
autoflush(
1
);
    my $msg_out 
=
 
"
hello world\n
"
;
    print 
"
out = 
"
, $msg_out;
    print SOCK $msg_out;
    my $msg_in 
=
 
<
SOCK
>
;
    print 
"
in = 
"
, $msg_in;
    close SOCK;
}
#
&
echoclient;
#exit(
0
);
for
 (my $i 
=
 
0
; $i 
<
 
9999
; $i
++
)
{
    echoclient;
}
我查看了lighttpd的实现,也是在创建完子进程之后才创建的epoll的fd.
请问谁知道哪里有讲解这个的文档?
这是美丽的分割线:
-----------------------------------------------------------------------
感谢luke, 他帮我解释了这个问题的原因:
假如fd1是由A进程加入epfd的,而且用的是ET模式,那么加入通知的是进程B,显然B进程不会对fd1进行处理,所以以后fd1的事件再不会通知,所以经过几次循环之后,所有的fd都没有事件通知了,所以epoll_wait在timeout之后就返回0了。而在客户端的结果可想而知,只能是被阻塞。
也就是说, 这是一种发生在epoll fd上面的类似于"惊群"的现象.

 

转载于:https://www.cnblogs.com/carekee/articles/2760735.html

你可能感兴趣的文章
动态规划 例子与复杂度
查看>>
查看oracle数据库的连接数以及用户
查看>>
【数据结构】栈结构操作示例
查看>>
中建项目环境迁移说明
查看>>
三.野指针和free
查看>>
activemq5.14+zookeeper3.4.9实现高可用
查看>>
TCP/IP详解学习笔记(3)IP协议ARP协议和RARP协议
查看>>
简单【用户输入验证】
查看>>
python tkinter GUI绘制,以及点击更新显示图片
查看>>
HDU4405--Aeroplane chess(概率dp)
查看>>
CS0103: The name ‘Scripts’ does not exist in the current context解决方法
查看>>
20130330java基础学习笔记-语句_for循环嵌套练习2
查看>>
Spring面试题
查看>>
窥视SP2010--第一章节--SP2010开发者路线图
查看>>
MVC,MVP 和 MVVM 的图示,区别
查看>>
C语言栈的实现
查看>>
代码为什么需要重构
查看>>
TC SRM 593 DIV1 250
查看>>
SRM 628 DIV2
查看>>
2018-2019-2 20165314『网络对抗技术』Exp5:MSF基础应用
查看>>