免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 16110 | 回复: 41
打印 上一主题 下一主题

分享自己做的一个系统工具--CME [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-08-27 20:35 |只看该作者 |倒序浏览
实在不知道该给这个东东起什么名字,但是想了想,跟主机通讯有关,而且采用了多线程机制,能高效点,就结合了我的QQ昵称,称之为
CME (cute messenger的意思)吧:wink:
联系方式:EMAIL:duanjigang1983@126.com
修改历史
###############################
2008-08-28 07:32【cme_scanner_v1.1.rar】: 参考Zer4tul 建议,cme.sh中"#! /usr/local/bin/expect" 改为 "#! /bin/env expect",原因,每个人机器上expect的安装目录可能不一致,为了程序的兼容性更好,也就是在安装时不需要修改cme.sh,进行此修改。
2008-09-03 13:25 【cme_scanner_v1.2.tar.gz】:根据建议ecjtubaowp,将函数"int init_thread_slot(struct thread_slot_t* thread_slot)"中的状态初始化语句  "thread_slot->thread_list . status=t_state_free;"挪到线程创建之前了,还有就是"do_work_dev(struct dev_t *pDev, FILE* fp)"函数中语句
  1. if(flag >= 1)
  2.                 {
  3.                         szline[strlen(szline) - 1] = '\0';
  4.                         fprintf(fp, "%s\n", szline);
  5.                         flag++;
  6.                 }
复制代码
改成
  1. if(flag >= 1)
  2.                 {
  3.                         fprintf(fp, "%s\n", szline);
  4.                         flag++;
  5.                 }
复制代码
,因为前面已经把换行符去掉了。要不然会少一个字符的
################################
附件是完整的代码,rar格式的,VS打开DSP文件,或者linux下直接make就行(大家顶啊^_^)


序言
     之前看到有好几个人发了用expect实现ssh,FTP自动登陆时遇到问题的帖子,自己也想搞明白这是怎么做的,可是一直身懒都没去涉及,
不幸的是(^_^),两周前项目中遇到一个类似问题,就花了几天时间匆匆完成了任务。
这几天闲了点,把与业务无关的部分剔除了,自己另做了个程序,比较通用,简单。主要功能就是:
利用expect脚本实现在远程服务器上执行命令,而cute messenger之所以加上了cute,因为在这里实现了用多线程对命令的
并发执行过程的封装,速度比单线程要快很多。
此贴的目的:

1):希望提供这个工具能帮助改进某些朋友的工作,如果可能的话,呵呵。
(2):其次,作为C语言多线程程序设计的一个应用例子吧,其实更细致点说,也是expect自动化程序在ssh服务上的一个应用实例。
(3):能够收获更多人有益的建议,对它进行改进。
(4):能提高本版的人气,众人拾柴火焰高嘛
:mrgreen:

初识SSH的惊喜与遗憾
从05年到现在三年使用ssh这个工具,我对它的几乎一无所知,就知道用ssh登录Linux系统执行点命令,或者直接窗口拖动传文件。直到前不久的一天,闲来无事,在linux下随便看看东西,man了一下ssh,发现有个command字眼,仔细看了下,嚄,原来ssh支持登录时传递命令阿。比如用
  1. ssh [email]root@192.168.1.100[/email] “date”
复制代码
就能查看192.168.1.100上的日期了。
呵呵,这个功能确实让我欢喜了一阵子,可是后来,又发现ssh有个缺点,就是不能自动登录,每次都要手动输入密码,太烦人了。加上前一段时间项目里经常涉及到好多台机器的相同操作,我越发觉得这个限制很烦人。于是便想搞个SSH自动登录。就在baidu上面找。
发现的大多数帖子都说要给目的机器拷贝一个密钥文件,这样以后就不需要输入密码了。
但是又觉得不对劲没,如果从A访问B时不需要输入密码,那么B机器岂不是很不安全,如果有人用ROOT登录了A机器,岂不是就跟登录了B机器一样,因为虽然他并不知道登陆B机器的密码,但是以前那个密钥文件却已经埋下了隐患。这个时候,恍然明白,原来我想要得并不是不需要输入密码的功能,而是能自动输入密码的功能。另外,还有一个问题,即时对其它Linux机器的访问不要密码,但是要实现自动登录(或者无密码登录),第一次总要手动拷贝密钥文件到目的机器,如果有2000台目标机器,岂不是会累死。所以我放弃了这种方法的尝试。
expect又带来希望
为了实现ssh自动登录功能,我又开始在网络上搜寻,在Q群里问了下,有人说用expect脚本可以做,便在baidu搜索expect脚本,真是兴奋阿,在CU某人的博客里找到了一个用epect实现的ssh自动登录的例子,其实就是一段代码,用expect程序来解释。
下面是一个自动登录制定主机并执行用户命令的例子:
  1. ###########    auto_login.sh  #####################
  2. 1        #!/usr/local/bin/expect
  3. 2        set PASSWD [lindex $argv 1]
  4. 3        set IP     [lindex $argv 0]
  5. 4        set CMD   [lindex $argv 2]
  6. 5        spawn ssh $IP $CMD
  7. 6        expect "(yes/no)?" {
  8. 7        send "yes\r"
  9. 8        expect "password:"
  10. 9        send "$PASSWD\r"
  11. 10        } "password:" {send "$PASSWD\r"} "*host " {exit 1}
  12. 11        expect eof
复制代码
我没有对expect的语法进行很详细的研究,只是大概理解了这段代码,下面根据自己的理解说下它的意思:
第一行制定使用/usr/local/bin目录下的expect命令对后面的程序进行解释。
第二行,三行,四行,分别从命令行参数中获取要登录的主机IP地址,登陆密码,以及要执行的命令。
第五行,大概就是要触发这样一个事件,执行ssh $IP $CMD命令。
第6行道第11行就是expect的整个交互过程了。
如果读取到(yes/no)?提示符,就输入yes并回车,如果读取到password:提示输入密码的字符串,就输入用户登录密码(root用户)。
当然如果不是第一次登陆,以前已经登录过的话,当输入ssh $IP $CMD回车后,会直接提示输入密码也就是说会读到字符串”* password:”,这个时候会输入密码回车(send "$PASSWD\r").
另外,如果主机不可达的话,(yes/no)?和”password:”的可能都不会出现,系统会提示:
“No route to host”这个时候,我们退出程序。
所以,如果你想查看192.168.1.100上的日期的话,并不需要直接登录,而只需要执行命令:
  1. ./auto_login.sh 192.168.1.100 password “date”
复制代码
就能看到结果了。
其实你可以用该方法来执行任何命令。
好了,我们现在可以稍微做点复杂的应用了,比如你有10台机器,想看看这10台机器上/tmp目录有多大了,决定是否要删除它们。
假设这10台机器的IP地址是以点分式存储在一个文件ip.conf中的,每行一个地址,而登录它们的root密码都是相同的,为123456,那么你就可以做这样一个脚本来完成你的任务:
  1. ############### single_thread_auto_run.sh  ############
  2. #!/bin/sh
  3. cat ip.conf | while read ip
  4. do
  5. echo “####### $ip #########” >> result.txt
  6. /usr/local/bin/expect auto_login.sh $ip 123456 “du –sh /tmp” >> result.txt
  7. echo “Running command on $ip over”
  8. done
复制代码
如果没有什么问题的话,对于10台机器也就是1分钟左右的时间或者四五十秒的时间就能够执行结束,而且结果会存储在result.txt文件中。
但是,现实情况并不像实验中的那么简单,现在大型企业的服务器或者Linux主机动不动几百台,或者动辄上千台,如果将上面的脚本应用到一个2000台主机的任务当中去,我测试过,通过auto_login.sh执行一个主机大概需要3-5秒的时间,这样理论上讲,对于2000台机器,在正常联通情况下,要串行执行完所有任务,也就是说在每台机器上完成这个任务,大概需要6000秒到1万秒的时间,大概为1小时40分钟到2小时40分钟的时间,这对于一个普通的网管人员或者上层管理人员来说肯定是不能忍受的。看来,expect是提供了曙光,可是并不能完全解决问题啊。继续郁闷^_^

多线程加速
在第三节的最后,我们落脚到了性能的瓶颈问题上,由于数量引起的性能下降是不可避免的,所以我们尝试寻找一种方法解决此问题,也就是提高性能。可能有的人立即会想到为何不启动多个single_thread_auto_run.sh程序?这个是一个不错的建议,可是启动多少个才算合适呢?而且这样也需要为每个进程分配制定的IP地址个数,而且每个进程执行的结果是独立的,进程数目少了执行慢,进程数目多了又需要把IP地址分成更多的份数,将来的结果又很分散,还需要手动合并或者又需要写程序去合并,这么做又会增加一些额外的难度和工作。
如果能让程序自动分配IP地址,并自己汇总执行结果,而且能执行更快的话,那更好了。
这个时候,我想到了用C实现的多线程程序来完成这个任务。
CME的设计与实现

CME的功能和原理其实很简单,它的目的就是实现高效的执行我们的shell脚本想要实现的功能,原理也没有多复杂,下面我大概描述大致的工作过程。
首先,程序从命令行读取IP地址的来源文件,登录密码和命令字符串。
接着,从数据文件中读取IP地址列表填充到数据结构中,也就是设备队列中。
下来,程序创建线程池对象。
然后,将设备队列中的所有IP地址对应的设备信息加入到线程队列中,平均分配给每个线程。
分配任务完成后,主线程等待线程池中工作线程执行结束。
线程池的工作线程都已经结束时,主线程遍历线程队列,打印每个线程执行的结果。
最后,退出程序。

数据结构
下面列出并介绍下程序中用到的5个结构体:
第一
struct config_t
{
    char data_file[FILE_NAME_LEN];//存储IP列表的文件

    char password[PASS_LEN];//存储密码

    char command[CMD_LEN];//存储命令

    int  dev_size;//设备数量

};
用来存储整个程序的配置信息。

第二 ,IP地址对应设备的抽象
struct dev_t
{
    unsigned long addr; //ip 地址

    int valid; //有效标志位

    int retur_value; //返回值类型

    char result_file[FILE_NAME_LEN];//存储返回结果的文件

    int id; 设备ID
};


第三,线程体的参数结构体
struct thread_param_t
{
    struct dev_t dev_list[MAX_THREAD_DEV];//该线程要处理的IP地址列表

    int dev_num;//IP地址的个数

    int valid;//参数有效性

};


第四,线程结构体,为了使程序不依赖于平台,采用了linux和windows通用的的结构定义
线程的创建也是linux和windows都支持的。
struct thread_t
{
#ifdef __LINUX__
    pthread_t thread_id;//线程标识符

#endif
#ifdef _WIN32
    DWORD thread_id;
#endif
    int thread_index; //线程编号

    enum thread_status_t status;//线程的执行状态

    struct thread_param_t parameter; //线程参数

    char result_file[FILE_NAME_LEN];//结果文件名

    int sunccess_num;//执行成功的数目

    int fail_num;//执行失败的数目

};


[ 本帖最后由 duanjigang 于 2008-9-3 14:12 编辑 ]

cme_scanner.rar

8.28 KB, 下载次数: 133

cme_scanner_v1.1.rar

8.27 KB, 下载次数: 127

cme_scanner_v1.2.tar.gz

6.12 KB, 下载次数: 176

论坛徽章:
0
2 [报告]
发表于 2008-08-27 20:46 |只看该作者

回复 #1 duanjigang 的帖子

有机会实践一下!

论坛徽章:
0
3 [报告]
发表于 2008-08-27 20:53 |只看该作者
第五:线程池的概念
struct thread_slot_t
{
    struct    thread_t thread_list[MAX_THREADS];//线程队列

    int     thread_num_run;//本意为运行的线程数,可在本程序中却没有用到,没有意义

};


关键代码片段
下面把比较重要和需要注意的代码片段列出来进行讲述。
第一:线程池创建,调用系统相关的线程创建方法来创建线程组,个数为50个或者在头文件中修改定义,对于这段代码,在windows下和linux下都是能编译通过并运行的,本程序唯一不能在windows上执行指出就是自动登录脚本的问题,因为我还没有尝试在windows上实现这个脚本,呵呵,没时间搭建windows上的expect环境。
第二:由于多线程并发引起的频繁连接以及关闭会导致系统迅速出现很多22端口的状态为TIME_WAIT的TCP连接,当数目达到较高时,ssh连接就会失败,所以需要修改系统设置,这里采用如下命令

  1. /sbin/sysctl -w .net.ipv4.tcp_max_syn_backlog=4096 > /dev/null
复制代码

第三,是多线程任务分配方法
在这里我们采用多线程平均分配的方法,而且在分配时,每个线程分配到的总是配置文件中相邻顺序的若干个IP地址,这样方便在打印结果时,使得每个IP地址的出现顺序与配置文件中的出现顺序一致。
比如2000个IP,100个线程,每个线程20个IP,第一个线程分配IP地址为1-20,第二个为21-40,第100个分配为1981-2000.
代码片段如下:
if(cme_config.dev_size % MAX_THREADS == 0)
    {
        per = cme_config.dev_size / MAX_THREADS;
        pos = MAX_THREADS;
    }else
    {
        per = cme_config.dev_size / MAX_THREADS + 1;
        pos =  MAX_THREADS + cme_config.dev_size -  MAX_THREADS * per;
    }
    
    if(per > MAX_THREAD_DEV)
    {
        printf("too many device [max = %d ,now = %d]\n", MAX_DEV, cme_config.dev_size);
        return 0;
    }
    
&nbsp;&nbsp;&nbsp;&nbsp;for(i = 0; i < MAX_THREADS; i++)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int j = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int max = (i < pos) ? per : (per - 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(j = 0; j < max; j++)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(&(thread_slot->thread_list[i].parameter.dev_list[j]),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&dev_list[nRet], sizeof(struct dev_t));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thread_slot->thread_list[i].parameter.dev_num++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thread_slot->thread_list[i].status = t_state_ready;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nRet++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}


第四,线程执行函数体
该模型比较简单,线程在自己的多个状态之间循环,该做什么事情做什么事情,如果为dead状态就退出,如果为ready状态就执行任务,然后修改为free状态。代码片段如下:
switch(pthread->status)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t_state_over:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t_state_free:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SLEEP(1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t_state_dead:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("thread %d now exits..\n", pthread->thread_index);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t_state_ready:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pthread->status = t_state_running;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do_work_thread(pthread);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pthread->status = t_state_free;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t_state_running:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SLEEP(1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}//end of switch

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;}//end of whil



[ 本帖最后由 duanjigang 于 2008-8-27 20:57 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2008-08-27 20:58 |只看该作者
第五,线程执行命令的过程
每个线程拿到自己的参数,从参数中挨个遍历设备队列,每拿到一个设备,就根据设备的IP地址,命令和设备的编号,构造一个shell语句,然后sytem执行,将结果定向到该设备对应的设备文件中。如此,直到所有的设备都执行完成,再遍历当前线程所负责的所有设备的结果文件列表,解析命令执行结果,将该组设备的执行结果存储到该线程的结果文件中。
第六:获取执行结果
很简单,主线程遍历线程池中每个线程的结果文件,打印每个线程体的结果文件的内容,便产生了整个程序的执行结果。
代码片段如下:
for(i = 0; i < MAX_THREADS; i++)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FILE* fp = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char szline[1024] = {0};
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct thread_t * pT = &(thread_slot->thread_list[i]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(pT->parameter.dev_num == 0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pT->status = t_state_free;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fp = fopen(pT->result_file, "r");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(fp)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nRet++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(fgets(szline, 1023, fp))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(szline);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(szline, 0, 1024);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fclose(fp);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}


程序退出时删除所有临时文件并停止线程池中的线程。
第七:主控线程过程如下:
int main(int argc, char* argv[])
{
&nbsp;&nbsp;&nbsp;&nbsp;//线程池

&nbsp;&nbsp;&nbsp;&nbsp;struct thread_slot_t cme_thread_slot;
&nbsp;&nbsp;&nbsp;&nbsp;//设备队列

&nbsp;&nbsp;&nbsp;&nbsp;struct dev_t cme_dev_list[MAX_DEV];
&nbsp;&nbsp;&nbsp;&nbsp;struct dev_t *pList = cme_dev_list;
&nbsp;&nbsp;&nbsp;&nbsp;int nRet = 0;
&nbsp;&nbsp;&nbsp;&nbsp;//初始化命令行参数

&nbsp;&nbsp;&nbsp;&nbsp;if(init_argv(argc, argv) <= 0)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;//初始化设备队列,从数据文件中读取所有的IP地址

&nbsp;&nbsp;&nbsp;&nbsp;//然后加入到pList中去

&nbsp;&nbsp;&nbsp;&nbsp;if((nRet = init_dev_list(pList)) <= 0)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;//printf("%d hosts initted\n", nRet);

&nbsp;&nbsp;&nbsp;&nbsp;//初始化线程池,启动线程的运行

&nbsp;&nbsp;&nbsp;&nbsp;if((nRet = init_thread_slot(&cme_thread_slot)) <= 0)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;//printf("%d threads created\n", nRet);

&nbsp;&nbsp;&nbsp;&nbsp;//将设备任务分配给线程池中的每个线程

&nbsp;&nbsp;&nbsp;&nbsp;if(add_dev_task(pList, &cme_thread_slot) <= 0)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto ret;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;//printf("add dev task successful!\n");

&nbsp;&nbsp;&nbsp;&nbsp;nRet = 0;
&nbsp;&nbsp;&nbsp;&nbsp;//等待线程执行结束

&nbsp;&nbsp;&nbsp;&nbsp;while(!thread_run_over(&cme_thread_slot))
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//printf("Waiting thread to running over %d seconds\n", nRet++);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SLEEP(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;//printf("Thread running over\n");

&nbsp;&nbsp;&nbsp;&nbsp;//读取线程执行结果

&nbsp;&nbsp;&nbsp;&nbsp;if(get_result_thread(&cme_thread_slot))
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//printf("get_result_thread fail\n");

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto ret;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;//printf("get_result_thread success\n");

ret:
&nbsp;&nbsp;&nbsp;&nbsp;//结束线程并删除临时文件

&nbsp;&nbsp;&nbsp;&nbsp;term_thread_slot(&cme_thread_slot);
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;  return 1;
}


[ 本帖最后由 duanjigang 于 2008-8-27 21:00 编辑 ]

论坛徽章:
0
5 [报告]
发表于 2008-08-27 21:03 |只看该作者
说明
本程序虽然是一个比较简单的版本,但是在windows和linux下却是都能编译通过,而且线程部分采用了多平台通用的实现方式,如果你在windows下能做好cme脚本文件的话,这个程序是完全可以运行在windows平台上的。
另外,如果要转载的话,请注明出处和作者啊^_^,欢迎感兴趣的朋友一起讨论。

=======================
对了,使用expect要安装tcl和expect的,这些软件的下载与安装方法,网上有很多的

[ 本帖最后由 duanjigang 于 2008-8-27 21:17 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2008-08-28 00:44 |只看该作者
好东西,收藏了。

给楼主一个建议,cme.sh里的#!/usr/local/bin/expect最好改成#!/bin/env expect

这样兼容性更好

论坛徽章:
0
7 [报告]
发表于 2008-08-28 07:25 |只看该作者
原帖由 Zer4tul 于 2008-8-28 00:44 发表
好东西,收藏了。

给楼主一个建议,cme.sh里的#!/usr/local/bin/expect最好改成#!/bin/env expect

这样兼容性更好

好的,修改一下
起初的想法是查找expect的路径,然后在程序运行时printf生成一个cme.sh出来,程序结束时再unlink掉。
最后觉得麻烦就这样放着了。

[ 本帖最后由 duanjigang 于 2008-8-28 07:31 编辑 ]

论坛徽章:
0
8 [报告]
发表于 2008-08-28 09:42 |只看该作者
感谢楼主分享啊~~~顶~~~~

论坛徽章:
0
9 [报告]
发表于 2008-08-28 09:53 |只看该作者
好东西帮顶一下

论坛徽章:
0
10 [报告]
发表于 2008-08-28 09:56 |只看该作者
有空试下吧。。之前装了BLFS感觉好好玩。。可惜自己技术不够。。应用不到实际生活
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP