最近学习了两个unix平台下两个软件的构架,其中一个就是大名鼎鼎的oracle,首先oracle的体系结构官方又很多资料,以前的大牛也分析了很多,我就不说了(因为说起了又是一篇文章)。我对它们的I/O处理有一些认识,猜测其使用的技术。基本是前端有个listener接受请求,让后将“请求”发给server进程(这个里面有复杂的session机制在里面)。unix下“请求”的本质就是“文件描述符”所以这篇文章的技术本质就是“进程间传输文件描述符”!
一下是我对中间件I/O处理的一个实现:
假设一个系统有大量的不同的I/O请求(这个在中间件开发中尤其常见),我们的怎个平台设计可以类oracle
listener----〉server当然可以是多对多的关系!
listener:这里我们可以做I/O路由,请求缓存,甚至可以是异步处理!
server:当然就是业务逻辑的处理。
实现:
listener创建一个tcp服务,一个map(做I/O路由),unix socket(用于和server之间传输fd),一块共享内存(用来缓存请求的数据)
server创建一个unix socket(用于接收listener发送的文件描述符)。
服务的流程:
client---(tcp)--〉listener---(unix socket)---〉server
server--〉结果返回给client
我实现了一份具体的代码,可以去这里下载:http://download.csdn.net/detail/littlegrizzly/4090915
以下就是进程间传输文件描述符需要的两个功能函数:
/*
* @file server_utility.c
* @author Luo ZhiHui <camel.flying@gmail.com>
* @version 1.0
*
* @section LICENSE
*
*
* @section DESCRIPTION
* Unix domain socket transfer multiple file descripers.
*/
#include <sys/types.h>
#include <sys/socket.h>
#define SEND_FD_NUMBER 2
#define HAVE_MSGHDR_MSG_CONTROL
#define UNIX_SOCKET_PATH "/tmp/transmissionFD"
ssize_t send_fd(int fd, void *ptr, size_t nbytes, int *send_fd_array, size_t send_fd_num)
{
struct msghdr msg;
struct iovec iov[1];
int index=0;
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int)*send_fd_num)];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len =CMSG_LEN(sizeof(int)*send_fd_num);
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
for(index=0; index<send_fd_num; index++)
{
*((int*)(CMSG_DATA(cmptr)+sizeof(int)*index)) = send_fd_array[index];
}
#else
msg.msg_accrights = (caddr_t)send_fd_array
msg.msg_accrightslen = sizeof(int)*send_fd_num;
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
return(sendmsg(fd, &msg, 0));
}
ssize_t recv_fd(int fd, void *ptr, size_t nbytes, int *recv_fd_array, size_t recv_fd_num)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
int index;
#ifdef HAVE_MSGHDR_MSG_CONTROL
union
{
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int)*(recv_fd_num))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
#else
int newfd;
msg.msg_accrights = (caddr_t)recv_fd_array;
msg.msg_accrightslen = sizeof(int)*recv_fd_num;
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ((n=recvmsg(fd, &msg, 0)) <= 0)
return(n);
#ifdef HAVE_MSGHDR_MSG_CONTROL
if((cmptr = CMSG_FIRSTHDR(&msg))!=NULL && cmptr->cmsg_len==CMSG_LEN(sizeof(int)*(recv_fd_num)))
{
if(cmptr->cmsg_level != SOL_SOCKET)
{
return -1;
}
if(cmptr->cmsg_type != SCM_RIGHTS)
{
return -2;
}
for(index=0; index<recv_fd_num; index++)
{
recv_fd_array[index] =*((int *)(CMSG_DATA(cmptr)+sizeof(int)*index));
}
}
else
{
recv_fd_array[0] = -1; /* descriptor was not passed */
}
#else
if(msg.msg_accrightslen == sizeof(int)*recv_fd_num)
{
;
}
else
{
recv_fd_array[0] = -1; /* descriptor was not passed */
}
#endif
return(n);
}
申明:以上代码载自unix环境高级编程,参考代码是传输一个fd,本人将其改成一次传输多个fd(不知道是不是一种改进)在linux和aix下测试过
关于以上代码的解释可以参照这里:http://www.lslnet.com/linux/f/docs1/i48/big5327458.htm。
总结:传输fd好像用得并不多,通过自己的测试,进程间传递文件描述符的代价还是太大,但是确实有很多事情可以去这样做。很常见的方案就是多个用户态线程协作,把I/O分离解决(当然这是对I/O密集型系统)。
参考资料:
steven unix高级环境编程,
unix网络编程I
unix网络编程II