Chinaunix首页 | 论坛 | 博客
  • 博客访问: 542682
  • 博文数量: 493
  • 博客积分: 2891
  • 博客等级: 少校
  • 技术积分: 4960
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 17:11
文章分类

全部博文(493)

文章存档

2010年(493)

分类:

2010-05-12 19:14:29

1 现象:问题描述
海外某局点升级某SCP的bill外挂程序后,产生很多僵尸进程。
2 关键过程:根本原因分析
将从现场收集到的数据反映,僵尸进程的父进程都为同一个后台常驻进程,且该进程由升级前的一次运行进程改为了后台常驻进程。但是开发者反映,这只是一个单进程程序,没有生成任何子进程。程序中没有任何fork的调用。通过紧张的分析,检查代码、分析程序中所有的系统调用,最后问题定位到程序检测磁盘占用的函数中:
bool CheckResource()
{
 …
#ifdef _LINUX_UNIX
    sprintf(command, "df -k %s | grep %s | awk '{ print $4 }'", pData, pData);
#endif
    g_Log.WriteLog(LOG_DEBUG, "command:%s", command);
 
    FILE * fp = NULL;
    fp = popen(command, "r");
    if (NULL == fp)
    {
     g_Log.WriteLog(LOG_ERROR, "CheckResource function popen command %s error", command);
        return false;
    }
 
 //读取标题行
    if (NULL == fgets(line, sizeof(line), fp))
    {
       fclose(fp);
       g_Log.WriteLog(LOG_ERROR, "CheckResource function fgets command %s result error", command);
       return false;
    }
    fclose(fp);
   …
}
我们可以看到,fp是通过popen调用生成的FILE指针,开发人员想当然的使用了fclose关闭fp文件描述指针。通过验证以及咨询HP工程师,由于popen会通过生成子进程执行sh命令,因此需要通过pclose来执行类似waitpid的功能,使得子进程可以完全退出而不会留下僵尸进程。
该程序升级前后调用此函数时都产生了僵尸,但升级前的版本由于执行一次就退出了没有被发现。而升级后为后台常驻进程,每天执行多次该函数,因此才被发现。
3 结论:解决方案及效果
将相应的fclose调用该为pclose。
if (NULL == fgets(line, sizeof(line), fp))
{
    pclose(fp);
    g_Log.WriteLog(LOG_ERROR, "CheckResource function fgets command %s result error", command);
    return false;
}
pclose(fp);
4 经验总结:预防措施和规范建议
对于FILE* 的文件指针不能想当然的使用fclose来关闭,需要看此指针是如何打开的。注意fopen/fclose以及 popen/pclose的配对使用。对于我们程序中使用的系统调用都不可大意,需要明确了解其功能和使用方法。
5 备注
6 考核点
popen与pclose必须配对使用。
7 试题
1、 关于使用调用popen说法正确的是:C
A) 使用popen后得到的FILE * 文件指针有系统负责释放资源,我们没有必要关注。
B) 使用popen后得到的FILE *文件指针可以使用fclose来释放资源,因为fclose就是用来关闭FILE* 文件指针的。
C) popen后得到的FILE *文件指针必须使用pclose来关闭,而不能使用fclose。
D) popen后得到的FILE *文件指针可以直接使用 delete 释放
阅读(1851) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~