注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

天地不仁,以万物为Googol!

天行有常,不以物喜,不以己悲……

 
 
 

日志

 
 

记上一笔  

2010-03-13 11:47:21|  分类: 积累 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
这两天帮同事看一个sshd退出时报错的问题。

问题的产生比较复杂。sshd出于安全的目的,当有ssh连入的时候,会先fork出一个root sshd来接收请求,并通过nss来鉴别用户权限。之后会再fork出一个sshd并把uid改为用户的uid,之后这个uid的sshd会fork出csh或者其他的sh等待用户输入。用户的每次动作都会到nss上请求权限。退出的时候,这几个进程其实是互相独立着退出的。问题出现在退出时,root sshd试图访问nss,但是收到EPIPE信号,且retry也失败。

原因是,申请对nss的链接的是root sshd,申请到nss链接后,会用atexit注册一个destory_fn的函数,并在这个函数里释放并销毁nss链接。而root sshd在fork uid sshd之后,两个进程同时持有同一个对nss的链接。这样在两个进程退出时,会对同一个链接释放两次。由于uid sshd动作比root sshd快一些,所以uid sshd对nss的释放是成功的,而root sshd由于还需要写一些和用户相关的logout信息,还需要查nss,导致了错误。

仔细研究发现,通过fork创建的进程,会复制一份父进程的fd数组。不同进程的fd在内核里会指向同一个socket链接(内核对链接应该维护了一个类似引用技术的东西)。如果其中某个进程退出了,内核会再创建一个新的fd供未退出的进程使用。再细节的就要去看kernel源码了。

但是由于uid sshd在退出时调用的destory_fn会通知nss服务器close链接,导致root sshd在对nss发送消息时会收到RST信号。这时虽然在root sshd里,nss的fd还存在,但已经处于关闭状态,自然再试图写的时候,就会得到EPIPE,而且由于retry只是对发送内容做retry,并没有重建链路的动作,因此也总是失败的。

目前的解决方法是使用pthread_atfork,在fork后对子进程做操作,把注册到atexit的destory_fn函数替换成只释放本地fd,不要求对端close的版本。话说pthread_atfork都能勾系统的fork函数了,这应该不仅仅是个线程库了吧?

后来发现,pthread_atfork在bsd的服务器上不起作用(不知道是bsd的原因还是api本身的原因)。解决的办法是,在申请链接的时候保存申请进程的pid,释放的时候检查本进程的pid与保存的pid是否一致。如果不一致就仅仅释放fd而不释放链接。只有pid一致的情况下才会释放链接,保证“谁申请谁释放”的原则。
  评论这张
 
阅读(1498)| 评论(3)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017