進程間傳遞文件描述符


unix一個傳統的服務器模型就是一連接一進程模型。進程對地址空間的保護作用是顯而易見的,為某個連接服務的進程出現錯誤導致崩潰,其它的進程還能正常的運行。但是,當連接數大時,

創建過多的進程顯然會影響效率。那么啟動一系列的進程,每個進程都利用epoll為多個連接服務,不是就可以避免創建過多的進程,同時也利用到了進程地址空間保護的優勢了嗎?

但是還有一個問題,我們不能為每一個進程都創建一個監聽套接口。

 

unix系統還有一個特性,就是可以將一個文件描述符傳遞給其它進程,方法有很多,unix域套接字是其中一種辦法。 

利用unix域套接字傳遞文件描述符,就可以用一個進程監聽套接口,將到來的連接傳遞給其它進程,由其它進程提供后續的服務。

本文介紹的模型主要結構是,一個listener進程,在啟動的時候創建數個data_worker進程,data_worker進程利用epoll為多個連接服務.listerner阻塞在listen調用上,每當一個新的連

接到來,就將accept到的套接字傳遞給一個data_worker,由data_worker為連接做后續服務. 

因為data_worker是利用了epoll為多個連接服務,所以避免了單進程單連接的確點。同時,一個data_worker的崩潰並不會影響到其它正常的data_worker.只要主控進程重新啟動一個

data_worker便可以了。

 

epoll.h

// C/C++ header file
// Author: root
// File: Epoll.h
// Created: 23:34:29 2008-05-05
// Modified: 05:14:35 2008-10-28
// Brief:

#ifndef _EPOLL_H
#define _EPOLL_H

#include <sys/epoll.h>
#include <error.h>
#include <stdio.h>
#include "noncopyable.h"
#include "singleton.h"


#define MAX_EPOLL 4096


class Epoll : private noncopyable
{


public:

Epoll():current_Size(0){}

void initEpoll(unsigned int _max)
{
this->_max = _max;
epfd = TEMP_FAILURE_RETRY(epoll_create(_max));

}

Epoll(unsigned int _max):_max(_max)
{
epfd = TEMP_FAILURE_RETRY(epoll_create(_max));
}

~Epoll()
{
TEMP_FAILURE_RETRY(close(epfd));
}


bool isFull()
{
return current_Size >= _max;
}


bool addEpoll(int fd,int eEvent)
{
if(current_Size >= _max)
return false;

epoll_event ev;
ev.data.fd = fd;
ev.events = eEvent;//EPOLLIN | EPOLLET;
int ret;
TEMP_FAILURE_RETRY(ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev));
++current_Size;
return true;

}

void delEpoll(int fd)
{
epoll_event ev;
ev.data.fd = fd;
TEMP_FAILURE_RETRY(epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&ev));
--current_Size;
}


/*
* para maxWait: -1:無限等待,0:馬上返回,>0:等待一定時間
*/
int wait(epoll_event *events,int maxWait = -1)
{
return TEMP_FAILURE_RETRY(epoll_wait(epfd,events,_max,maxWait));

}


private:
int epfd;
unsigned int current_Size;
unsigned int _max;
};

#endif

listener.cpp

#include "SocketWrapper.h"


int main(int argc, char ** argv)
{
if(argc < 2)
{
printf("server port/n");
return 0;
}

//創建子進程,創建域套接字
int unsock = create_un_execl("./data_worker","data_worker");
struct sockaddr_in servaddr;
int fd;
if((fd = Socket_Bind_Listen(Inet,Stream,Tcp,NULL,atoi(argv[1]),servaddr,5)) > 0)
{
for( ; ;)
{
struct sockaddr_in cliaddr;
socklen_t len;
int connfd = Accept(fd,(struct sockaddr*)NULL,NULL);
if(connfd > 0)
{

char address[128];
unsigned short port;

getRemoteAddrPort(connfd,&cliaddr,&len,address,port);

//將套接字傳遞給data_worker
write_fd(unsock,(void*)"",1,connfd);

printf("ip:%s,port:%d/n",address,port);
//listener 可以關閉那個套接字了
close(connfd);
}
}
}

return 0;
}

data_worker.cpp

#include "SocketWrapper.h"    
#include "Epoll.h"


int main(int arcv, char **argv)
{

int ufd=atoi(argv[1]);//unix domain 描述字

Epoll _epoll(100);
_epoll.addEpoll(ufd,EPOLLIN);
for( ; ; )
{
epoll_event events[4096];
int nfds = _epoll.wait(events,-1);
for( int i = 0; i < nfds; ++i)
{
if(events[i].events & EPOLLIN)
{
if(events[i].data.fd == ufd)
{
//listener傳來一個連接,將這個連接添加到epoll中
int fd;
char c;
if(read_fd(ufd,&c,1,&fd) >= 0)
{
_epoll.addEpoll(fd,EPOLLIN);
}
}
else
{
char buf[1024];
bzero(buf,sizeof(buf));
int fd = events[i].data.fd;
int ret = read(fd,buf,1024);
if(ret > 0)
{
struct sockaddr_in cliaddr;
socklen_t len;

char address[128];
unsigned short port;

getRemoteAddrPort(fd,&cliaddr,&len,address,port);

printf("from %s:%d,%s/n",address,port,buf);
}
else if(ret == 0)
{
_epoll.delEpoll(fd);
close(fd);
}

}

}

}

}
}






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM