随笔-103  评论-224  文章-30  trackbacks-0
 
     摘要:    由于linux内核中的struct list_head已经定义了指向前驱的prev指针和指向后继的next指针,并且提供了相关的链表操作方法,因此为方便复用,本文在它的基础上封装实现了一种使用开链法解决冲突的通用内核Hash表glib_htable,提供了初始化、增加、查找、删除、清空和销毁6种操作,除初始化和销毁外,其它操作都做了同步,适用于中断和进程上下文。...  阅读全文
posted @ 2015-09-15 17:18 春秋十二月 阅读(2117) | 评论 (0)编辑 收藏
   nginx的域名解析器使用已连接udp(收发前先调用ngx_udp_connect)发送dns查询、接收dns响应,如上篇tcp异步连接所讲,iocp需要先投递udp的接收操作,才能引发接收完成的事件,因此要对域名解析器和udp异步接收作些改进。

发送后投递
    dns查询由ngx_resolver_send_query函数实现,定义在core/ngx_resolver.c中。
 1static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)
 2{
 3    
 4     if (rn->naddrs == (u_short) -1{
 5        n = ngx_send(uc->connection, rn->query, rn->qlen);
 6    
 7  }

 8
 9#if (NGX_HAVE_INET6)
10  if (rn->query6 && rn->naddrs6 == (u_short) -1{
11      n = ngx_send(uc->connection, rn->query6, rn->qlen);
12    
13  }

14#endif
15
16#if (NGX_WIN32) 
17    if (ngx_event_flags & NGX_USE_IOCP_EVENT){
18        uc->connection->read->ready = 1;
19        ngx_resolver_read_response(uc->connection->read);
20}

21#endif
22
23  return NGX_OK;
24}
    当nginx用于代理连接上游服务器前,要先解析域名,首次调用链为:ngx_http_upstream_init_request->ngx_resolver_name->ngx_resolver_name_locked->ngx_resolver_send_query;若5s(单次超时)后还没收到dns响应,则再发送1次查询,调用链为:ngx_resolver_resend_handler->ngx_resolver_resend->ngx_resolver_send_query,如此反复,直到收到响应或30s(默认总超时)后不再发送查询。它调用ngx_send发送dns查询,16行~21行代码为笔者添加,ngx_resolver_read_response函数用于接收并分析dns响应报文,它会调用到下面的ngx_udp_overlapped_wsarecv函数。

异步接收
   由ngx_udp_overlapped_wsarecv函数实现,定义在os/win32/ngx_udp_wsarecv.c中。
 1ssize_t ngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)
 2{
 3    int             flags, rc;
 4    WSABUF          wsabuf;
 5    ngx_err_t       err;
 6    ngx_event_t    *rev;
 7    WSAOVERLAPPED  *ovlp;
 8    u_long             bytes;
 9    
10    rev = c->read;
11    
12    if (!rev->ready) {
13      ngx_log_error(NGX_LOG_ALERT, c->log, 0"ngx_udp_overlapped_wsarecv second wsa post");
14      return NGX_AGAIN;
15  }

16    
17  if (rev->complete) {
18   if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
19       if (rev->ovlp.error && rev->ovlp.error != ERROR_MORE_DATA{
20           ngx_connection_error(c, rev->ovlp.error, "ngx_udp_overlapped_wsarecv() failed");
21           return NGX_ERROR;
22       }

23   }

24   
25   rev->complete = 0;
26  }

27     
28    ovlp = NULL;
29    wsabuf.buf = (CHAR *) buf;
30    wsabuf.len = (ULONG) size;
31    flags = 0;
32    
33retry:    
34    rc = WSARecv(c->fd, &wsabuf, 1, (DWORD*)&bytes, (LPDWORD)&flags, ovlp, NULL);
35        
36    if (rc == -1{
37         rev->ready = 0;
38         err = ngx_socket_errno;
39    
40       if (err == WSA_IO_PENDING) {
41             return NGX_AGAIN;
42         }

43    
44       if (err == WSAEWOULDBLOCK) {
45           if (ngx_event_flags & NGX_USE_IOCP_EVENT) {                
46                 rev->ovlp.type = NGX_IOCP_IO;
47                 ovlp = (WSAOVERLAPPED *)&rev->ovlp;
48                 ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));
49                
50                 wsabuf.buf = NULL;
51                 wsabuf.len = 0;
52                 flags = MSG_PEEK;
53                
54                goto retry;            
55            }

56            
57            return NGX_AGAIN;            
58        }

59            
60        ngx_connection_error(c, err, "ngx_udp_overlapped_wsarecv() failed");    
61        rev->error = 1;
62    
63        return NGX_ERROR;
64    }

65    
66    if ((ngx_event_flags & NGX_USE_IOCP_EVENT) && ovlp) {
67        rev->ready = 0;
68        return NGX_AGAIN;
69    }

70    
71    return bytes;
72}
   先以非阻塞方式接收,若发生WSAWOULDBLOCK错误,则使用MSG_PEEK标志投递一个0字节的重叠接收操作,当dns响应返回时发生完成事件,会再次进入ngx_resolver_read_response而调用到该函数,此时rev->complete为1,rev->ovlp.error为ERROR_MORE_DATA(GetQueuedCompletionStatus返回的错误),由于使用了MSG_PEEK,因此数据还在接收缓冲区中,要忽略ERROR_MORE_DATA而继续接收,这时就能成功了。不管WSARecv返回WSA_IO_PENDING错误还是成功,iocp都会得到完成通知,所以这里当重叠操作投递成功时,返回NGX_AGAIN,便于在回调内统一处理。
posted @ 2015-06-25 17:01 春秋十二月 阅读(5922) | 评论 (0)编辑 收藏
   iocp是Windows NT操作系统的一种高效IO模型,对应于Linux中的epoll和FreeBSD中的kqueue,nginx对ske(select、kqueue和epoll的首写字母组合)的支持很好,但截止到1.6.2版本,还不支持iocp。由于ske都是反应器模式,即先注册IO事件,当IO事件发生(读写通知)时,在其回调内主动调用API来读或写数据;而iocp是前摄器模式,要先投递IO操作,才能引发IO事件(完成通知)的发生,在其回调内数据已被动由操作系统读或写完成。因此,iocp的特点决定了nginx对它的支持与ske有所不同。通过hg clone http://hg.nginx.org/nginx下载的nginx源代码,虽然实现了iocp事件模块、异步接受连接、部分异步读写,但根本不能正常工作,而且不支持异步连接和SCM服务控制,笔者在参考ske模块的实现基础上,改进支持了如下特性:
      1. 异步接受连接时的负载均衡
      2. 正反向代理的异步连接
      3. 异步聚合读写
      4. 域名解析时的UDP异步接收
      5. 异步文件传输
      6. SCM服务控制
   由于2、4、6均为原创,其它几点的思路皆源于ske模块的实现(只是平台API不同),因此本文先阐述异步连接的实现。为了兼容select事件模块,所有iocp相关的代码使用NGX_HAVE_IOCP宏和(或)NGX_USE_IOCP_EVENT标志包围,其中NGX_HAVE_IOCP宏用于条件编译,在WIN32平台下,定义为1;当选择的事件模块为iocp时,全局变量ngx_event_flags才包含NGX_USE_IOCP_EVENT标志。

异步连接对端
   由ngx_event_connect_peer函数(这里省去了与异步连接无关的代码)实现,定义在event/ngx_event_connect.c中,因为connect不支持异步连接事件的完成通知,所以要使用扩展API ConnectEx。 
 1ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc)
 2{
 3    int                rc;
 4    ngx_int_t          event;
 5    ngx_err_t          err;
 6    ngx_uint_t         level,family;
 7    ngx_socket_t       s;
 8    ngx_event_t       *rev, *wev;
 9    
10    s = ngx_socket(family = pc->sockaddr->sa_family, SOCK_STREAM, 0);
11              
12    #if (NGX_HAVE_IOCP)
13    if((pc->local==NULL||pc->local->sockaddr->sa_family != family) 
14        && (ngx_event_flags & NGX_USE_IOCP_EVENT)){
15        if(ngx_iocp_set_localaddr(pc->log,family,&pc->local) != NGX_OK)
16            goto failed;    
17    }

18    #endif
19       
20    
21    #if (NGX_HAVE_IOCP)
22    if(ngx_event_flags&NGX_USE_IOCP_EVENT){        
23        LPWSAOVERLAPPED   ovlp;
24        ovlp = (LPWSAOVERLAPPED)&wev->ovlp;
25        ngx_memzero(ovlp,sizeof(WSAOVERLAPPED));
26        wev->ovlp.type = NGX_IOCP_CONNECT;
27        rc = ngx_connectex(s,pc->sockaddr,pc->socklen,NULL,0,NULL,ovlp) ? 0 : -1;
28    
29    }
else
30        rc = connect(s, pc->sockaddr, pc->socklen);
31    #else
32      rc = connect(s, pc->sockaddr, pc->socklen);
33    #endif
34    
35    if (rc == -1{
36        err = ngx_socket_errno;
37        if (err != NGX_EINPROGRESS
38    #if (NGX_WIN32)
39        /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
40        && err != NGX_EAGAIN
41    #if (NGX_HAVE_IOCP)
42        && err != WSA_IO_PENDING
43    #endif
44    #endif
45            ){
46            
47            ngx_log_error(level, c->log, err, "connect() to %V failed", pc->name);
48            ngx_close_connection(c);
49            pc->connection = NULL;
50          
51            return NGX_DECLINED;
52        }

53    }

54    
55}
   调用ConnectEx前要先bind本地地址,不然发生WSAEINVAL错误;由于域名解析可能返回IPv6记录,导致创建本地套接字的地址族为AF_INET6,因此bind时需要匹配IPv6地址,不然发生WSAEFAULT错误,导致nginx返回Internal Server Error错误给前端,因此绑定前要调用ngx_iocp_set_localaddr设定正确的本地地址,当且仅当pc->local为空或地址族不匹配时。

本地初始化与设定
   支持IPv6,实现在event/modules/ngx_iocp_module.c。
   地址变量定义如下。
1static struct sockaddr_in  sin;
2#if (NGX_HAVE_INET6)
3static struct sockaddr_in6  sin6;
4#endif
5static ngx_addr_t           local_addr;
   sin对应IPv4,sin6对应IPv6,作为bind的套接字本地地址。

   sin和sin6在启动iocp事件模块时调用ngx_iocp_init初始化。   
 1static ngx_int_t ngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer)
 2{
 3    
 4  sin.sin_family = AF_INET;
 5  sin.sin_port = 0;
 6  sin.sin_addr.s_addr = INADDR_ANY;
 7    
 8#if (NGX_HAVE_INET6)
 9    sin6.sin6_family = AF_INET6;
10    sin6.sin6_port = 0;
11    sin6.sin6_addr = in6addr_any;
12#endif
13    
14  local_addr.name.len = sizeof("INADDR_ANY"- 1;
15  local_addr.name.data = (u_char *)"INADDR_ANY";    
16    
17}
   不论IP地址或端口,都指定为0,表示由系统自动分配出口IP地址和未占用的端口。

   本地设定由ngx_iocp_set_localaddr实现。
 1ngx_int_t ngx_iocp_set_localaddr(ngx_log_t *log, in_port_t family, ngx_addr_t **local)
 2{
 3    struct sockaddr *sa;
 4    socklen_t len;
 5    
 6    if(AF_INET == family){            
 7        sa = &sin;
 8        len = sizeof(struct sockaddr_in);        
 9    }

10#if (NGX_HAVE_INET6)
11    else if(AF_INET6 == family){
12        sa = &sin6;
13        len = sizeof(struct sockaddr_in6);
14    }

15#endif
16    else{
17        ngx_log_error(NGX_LOG_ALERT, log, 0"not supported address family");
18        return NGX_ERROR;        
19    }

20
21    local_addr.sockaddr = sa;
22    local_addr.socklen = len;
23    *local = &local_addr;
24
25    return NGX_OK;
26}
   对于除IPv4和IPv6外的协议族,则记录一个错误日志。必要时也可扩展支持其它的协议族,例如NetBIOS(对应地址族为AF_NETBIOS),但要看ConnectEx是否支持。
posted @ 2015-06-24 17:02 春秋十二月 阅读(7004) | 评论 (1)编辑 收藏
   ICMP在IP系统间传递差错和管理报文,是任何IP系统必须实现的组成部分。Linux 2.6.34中ICMP模块的实现在linux/icmp.h,net/icmp.h和ipv4/icmp.c中,导出了icmp_err_convert数组和icmp_send函数,供其它网络子系统使用。在其它网络子系统中,当检测到错误时,调用icmp_send产生并发送相应的ICMP差错消息到源主机;当源主机收到ICMP不可达差错消息,传递到原始套接字和传输层,而它们使用icmp_err_convert把对应的消息代码转换成套接字层比较容易理解的错误代码。在内核空间中可发送的ICMP消息包括查询应答和差错报文,下面总结了产生这两类消息的网络子系统(及函数)与错误转换。


应答消息
   应答消息由ICMP模块的内部函数icmp_reply而非icmp_send发送。根据RFC1122 3.2.2.9规范, 除非一个主机作为地址掩码代理,否则不能发送回复,这对应ICMP的icmp_address实现为空,因此上表没有列出地址掩码应答项(内核符号为ICMP_ADDRESSREPLY)。

差错消息
   差错消息由中间路由器或目的主机产生,当数据报不能成功提交给目的主机时。从上表可见,在IP层的接收、本地处理、转发和输出各过程中,都可能产生差错消息;在传输层如果对应的端口没有打开,那么UDP会产生ICMP端口不可达差错,而TCP则会使用自己的差错处理机制发送一个RST复位包,这也是上表没有列出TCP子系统的原因。对于重定向差错,由ICMP模块的icmp_redirect调用ip_rt_redirect更新路由;其它差错则由icmp_unreach处理。


错误转换
   第2列为icmp_err_convert数组索引,第4列也就是调用socket API出错时返回的errno,最后1列为icmp_err_convert中的fatal成员取值,0表示非致命错误,1表示致命错误,需要报告给用户进程。错误转换会被RAW的raw_err、TCP的tcp_v4_err和UDP的udp_err用到,对于ICMP_DEST_UNREACH类型的差错,使用上表转换;ICMP_SOURCE_QUENCH类型的忽略不处理;ICMP_PARAMETERPROB类型的转换成EPROTO(协议错误);ICMP_TIME_EXCEEDED类型的转换成EHOSTUNREACH
   在这要注意,从ICMP_PORT_UNREACH到ECONNREFUSED的转换,不适用于TCP,原因已在上节说明;而对于UDP的未连接套接字,如果主机在线而端口没打开,调用sendto得不到ECONNREFUSED错误,但recvfrom会阻塞,这是因为虽然内核收到了ICMP差错,但没上报给应用进程。尽管如此,如果想得到ECONNREFUSED错误,那么可以写个ICMP守护进程,应用进程先把它的套接字描述符通过unix域套接口传递到ICMP守护进程,而守护进程使用raw socket来接收ICMP差错,再发给应用进程。


发送限速
   不论一般差错消息还是重定向差错消息,发送限速针对的都是特定目标主机。
   一般限速
   在使用icmp_send发送差错消息(PMTU消息除外)时,为减少网络拥塞而限制了发送的速率,限速由xrlim_allow函数实现,定义在ipv4/icmp.c中。
 1#define XRLIM_BURST_FACTOR 6
 2int xrlim_allow(struct dst_entry *dst, int timeout)
 3{
 4    unsigned long now, token = dst->rate_tokens;
 5    int rc = 0;
 6
 7    now = jiffies;
 8    token += now - dst->rate_last;
 9    dst->rate_last = now;
10    if (token > XRLIM_BURST_FACTOR * timeout)
11        token = XRLIM_BURST_FACTOR * timeout;
12    if (token >= timeout) {
13        token -= timeout;
14        rc = 1;
15    }

16    dst->rate_tokens = token;
17    return rc;
18}
   dst为目标路由缓存,timeout为允许发送的超时(单位为jiffies),dst->rate_tokens记录令牌的个数,当令牌个数不小于timeout时,则减少timeout并允许发送一个消息;反之则不能发送,需等到令牌个数累积到大于timeout时才能发送,但是不能无限大,否则就会导致在一个可能很短的timeout内,发送远多于6个的消息,引起ICMP风暴,所以这里限制了令牌的最大值为XRLIM_BURST_FACTOR*timeout即6倍的超时,也就是说在一个timeout内,最多能发送6个差错消息。 
  
   重定向限速
   路由子系统使用ip_rt_send_redirect来发送重定向消息,定义在ipv4/route.c中,该函数内部调用icmp_send实现,在它的限速基础上,使用指数回退算法控制发送速率。
 1void ip_rt_send_redirect(struct sk_buff *skb)
 2{
 3    struct rtable *rt = skb_rtable(skb);
 4    
   
 5
 6    /* No redirected packets during ip_rt_redirect_silence;
 7     * reset the algorithm.
 8     */

 9    if (time_after(jiffies, rt->u.dst.rate_last + ip_rt_redirect_silence))
10        rt->u.dst.rate_tokens = 0;
11
12    /* Too many ignored redirects; do not send anything
13     * set u.dst.rate_last to the last seen redirected packet.
14     */

15    if (rt->u.dst.rate_tokens >= ip_rt_redirect_number{
16        rt->u.dst.rate_last = jiffies;
17        return;
18    }

19
20    /* Check for load limit; set rate_last to the latest sent
21     * redirect.
22     */

23    if (rt->u.dst.rate_tokens == 0 || time_after(jiffies, (rt->u.dst.rate_last (ip_rt_redirect_load << rt->u.dst.rate_tokens)))) {
24        icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
25        rt->u.dst.rate_last = jiffies;
26        ++rt->u.dst.rate_tokens;
27        
28    }

29}
   重定向差错使用ip_rt_redirect_silence(默认为(HZ/50)<<10)、ip_rt_redirect_number(默认为9)和ip_rt_redirect_load(默认为HZ/50)3个量来控制发送的速率;rt->u.dst.rate_last记录上次发送的时间,rt->u.dst.rate_tokens累计发送总数,最大值为ip_rt_redirect_number;当两次发送的时间间隔超过ip_rt_redirect_silence或ip_rt_redirect_load<<rt->u.dst.rate_tokens,并且发送总数不超过ip_rt_redirect_number时,才允许发送一个,这样一来,在ip_rt_redirect_silence间隔内,每次发送的超时呈2的指数增长,达到了变减速发送的效果,直到总数达到ip_rt_redirect_number时停止发送,这是因为源主机可能忽略了重定向消息所以停止发送;当ip_rt_redirect_silence时间过后,又允许发送了,这是因为认为源主机没有更新路由所以又需要发送。
posted @ 2015-05-18 19:52 春秋十二月 阅读(2551) | 评论 (0)编辑 收藏
     摘要:    接上篇初始化与创建,本篇阐述Socket操作和销毁两部分的实现。 Socket操作    系统调用read(v)、write(v)是用户空间读写socket的一种方法,为了弄清楚它们是怎么通过VFS将请求转发到特定协议的实现,下面以read为例(write同理),并假定文件描述符对应的是IPv4 TCP类型的socket...  阅读全文
posted @ 2015-05-03 16:55 春秋十二月 阅读(4993) | 评论 (0)编辑 收藏
     摘要: 引言    在Unix的世界里,万物皆文件,通过虚拟文件系统VFS,程序可以用标准的Unix系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作。对于网络套接字socket也是如此,除了专属的Berkeley Sockets API,还支持一些标准的文件IO系统调用如read(v)、write(v)和close等。那么为什么socket也支持文件IO系统调...  阅读全文
posted @ 2015-05-03 16:31 春秋十二月 阅读(8181) | 评论 (0)编辑 收藏
     摘要: 字符集合     依据RFC3986 2规范,HTTP URI中允许出现的US-ASCII字符的子集,可以分成保留、未保留及转义这几类,每类的全部字符列表如下       ● 保留:  : / ? # [ ] @ ! $ & '( ) * + ,; =共18个,一般...  阅读全文
posted @ 2015-02-10 18:40 春秋十二月 阅读(4503) | 评论 (1)编辑 收藏
     摘要: 脚本概述   nginx是一款著名的开源web服务器,为方便升级与恢复,编写了一个简单的脚本,因为升级备份了可执行文件和配置文件(后缀名为old),所以可用于恢复。当升级时,若nginx正在运行,则不中断服务进行平滑升级,否则直接拷贝覆盖;当恢复时,若nginx正在运行,则不中断服务进行平滑恢复,否则直接拷贝覆盖。是否正在运行根据pid来判断,而pid从pid文件读取...  阅读全文
posted @ 2015-01-19 00:36 春秋十二月 阅读(1883) | 评论 (0)编辑 收藏
     摘要:    本文描述了一种简单的跨平台锁框架的设计与实现,该框架小巧实用、易于扩展,它的特点如下:      ● 实现了线程间互斥锁      ● 实现优化了单线程环境中的空锁和空级别锁      ● 支持编译时或运行时选择锁   ...  阅读全文
posted @ 2014-12-28 23:38 春秋十二月 阅读(2230) | 评论 (6)编辑 收藏
   众所周知,TLS是指线程局部存储,FIFO是Unix中的命名管道,可用于无关进程间的通信,而本文描述的TLS FIFO是指这样一种机制:如果一个线程在每次IO操作时,若没有连接,则先连接到FIFO服务端,再将连接关联到这个线程的TLS中,这里的连接即创建并打开唯一的FIFO,之后的读写就在这个FIFO连接上进行;当FIFO连接断开时,在下次IO操作时会自动重连。这样一来,用户程序就只要调用相关的IO操作,而不必管理连接,极大地简化了程序。使用FIFO通信前先要创建FIFO再打开它,其中创建是最重要的操作,结果有3种情况:成功、失败和已存在。

结构定义
typedef struct 
{
    int fd;
    char *name;
}ipc_fifo_t;
  fd存储FIFO文件描述符,name存储FIFO文件系统路径名。

接口函数
   创建FIFO
ipc_fifo_t* ipc_fifo_make(const char *path,mode_t mode);
  path指定FIFO路径,可以是绝对路径或相对路径,mode指定访问权限,若成功则返回一个FIFO结构,否则为NULL;通常被ipc_fifo_open调用。
   
   打开FIFO
int ipc_fifo_open(ipc_fifo_t **f,const char *path,int flag,mode_t mode);
  flag指定打开标志,如果包含了O_CREAT标志,那么调用ipc_fifo_make创建新的FIFO对象并在打开成功后替换*f,否则如果*f为空,就分配并初始化一个fifo结构;mode指定访问权限,仅当创建时生效。虽然f为输入输出参数,但操作失败时不会影响它,也就是说没有副作用。

  ● 发送数据   
ssize_t ipc_fifo_write(ipc_fifo_t *f,const void *data,size_t size);
  如果成功则返回已发送的字节数,否则返回-1,errno表示出错代码。
   
  ● 接收数据
ssize_t ipc_fifo_read(ipc_fifo_t *f,void *data,size_t size);
  如果成功则返回已发送的字节数,否则返回-1,errno表示出错代码。

  ● 关闭FIFO
void ipc_fifo_close(ipc_fifo_t *f);
  当通信结束的时候,应该调用此函数来关闭FIFO,它会先删除FIFO文件和关闭文件描述符,最后释放fifo结构。

  ● 获取TLS FIFO
ipc_fifo_t* ipc_fifo_tls_get();
  该函数一般被发送数据接口调用,若成功则返回一个FIFO结构,否则为NULL;每个线程对应一个FIFO对象,对于同一线程,获取的是同一个FIFO对象,而后便可调用ipc_fifo_write来发送数据。

工作流程
  创建FIFO
  适用于FIFO客户端和服务端,但服务端由于要异步处理众多FIFO客户端,因此要注意以下2个问题,这也是使用FIFO技术通信的一些细节。
  1)必须以非阻塞读写方式打开知名FIFO,即以O_CREAT|O_RDWR|O_NONBLOCK标志来调用ipc_fifo_open,这样才不会阻塞等待某个客户端以同步写方式打开知名FIFO而返回,因为它使用O_RDWR标志,这样自己既读又写,加上O_NONBLOCK,就立即返回了。
  2)必须以非阻塞只读方式打开对应客户端FIFO,即以O_RDONLY|O_NONBLOCK标志调用ipc_fifo_open,这样就不会阻塞接受客户端建立连接而返回。

  建立FIFO连接
  适用于FIFO客户端,被发送数据接口调用,考虑到服务端可能事先没有打开知名FIFO来监听连接,所以这里先以异步方式写打开知名FIFO如果成功则改以阻塞方式发送唯一路径名到服务端,如果发送完全后,接着以同步方式写打开唯一FIFO,这是为了等待服务端打开了对应的唯一FIFO。

  发送数据

  适用于FIFO客户端,当TLS中没有关联对应的FIFO时,则先调用fifo_tls_get进入建立FIFO连接流程,而后再发数据。
posted @ 2014-12-01 00:13 春秋十二月 阅读(1148) | 评论 (2)编辑 收藏
仅列出标题
共11页: 1 2 3 4 5 6 7 8 9 Last