Chinaunix首页 | 论坛 | 博客
  • 博客访问: 60529
  • 博文数量: 8
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 82
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-04 17:00
文章分类

全部博文(8)

文章存档

2016年(5)

2015年(3)

我的朋友

分类: LINUX

2015-12-16 11:44:41

IO复用技术常常用于网络服务器中,用来处理大量的客户端数据。Linux下有select,epoll,Unix下有kqueue,Windows下有IOCP。我们这里只讨论Linux环境下的。
提到IO复用,大部分人都会想到网络,TCP,socket等名词,很少跟嵌入式,串口等沾上边。但Linux有个非常好的特性,就是“一切皆文件”。线程对socket的监听,其实是对一个文件的监听。而串口也是文件,对线程来说,监听socket跟监听串口是没区别的,他们都是文件描述符。所以,我们也可以用IO复用技术来监听串口,处理串口数据。
Linux下有select和epoll这两个选择(还有一个poll,比较少人用)。Select会随着监听的文件数量增加而线性增加轮询时间,epoll则不会。当监听的数量到达一定程度的时候,select处理的速度就跟不上epoll了,所以服务器一般都用epoll。但在这里我们只用来监听串口,用epoll有点大材小用了,一般的板子都只留了几路串口,轮询时间基本可以忽略,用select来处理足够了。
先来构建一个串口基类,所有串口都通过这个基类派生:

  1. class SERIAL
  2. {
  3. public:
  4.     SERIAL();
  5.     virtual ~SERIAL();

  6.     //打开串口
  7.     bool open_serial(const char* dev_name);
  8.     //设置串口波特率
  9.     void set_baudrate(int baud);
  10.     //设置数据位,停止位,校验位
  11.     bool set_parity(int databits,int stopbits,int parity);
  12.     //关闭串口
  13.     bool close_serial();
  14.     //虚函数,处理串口数据,由继承类实现
  15.     virtual void deal_data(const char* serial_data,int len);

  16.     int serial_fd;
  17. };
注意,这个SERIAL基类的deal_data函数定义成了虚函数,它的实现是这样的:

  1. void SERIAL::deal_data(const char* serial_data,int len)
  2. {
  3.     return ;
  4. }
里面是空的,是由于每个串口的数据格式不一样,比如串口1是GPS数据,串口1派生类的deal_data函数就要处理GPS数据,串口2是其它格式的数据,串口2派生类的deal_data就要能处理其它格式的数据。deal_data函数在派生类中实现,在监听的时候再利用虚函数的多态性来指定要用哪个。
我们顺手来派生两个串口类:

  1. class serial_1:public SERIAL
  2. {
  3. public:
  4.     serial_1();
  5.     ~serial_1();
  6.     
  7.     //串口1数据处理函数,继承于串口基类
  8.     virtual void deal_data(const char* serial_data,int len);
  9. };
  1. class serial_2:public SERIAL
  2. {
  3. public:
  4.     serial_2();
  5.     ~serial_2();
  6.     
  7.     //串口2数据处理函数,继承于串口基类
  8.     virtual void deal_data(const char* serial_data,int len);
  9. };
接下来构建一个IO复用的监听类LISTEN_SERIAL:

  1. #define MAX_SERIAL 10    //最多监听的串口数量
  2. class LISTEN_SERIAL
  3. {
  4. public:
  5.     LISTEN_SERIAL();
  6.     ~LISTEN_SERIAL();
  7.     
  8.     //开始监听串口线程
  9.     void start_listen();
  10.     static void* listen_serial(void* pointer);
  11.     //把串口加入到监听队列
  12.     void get_serial(SERIAL* s);
  13.     //比较两个文件描述符,返回较大的一个
  14.     int get_maxfd(int fd1,int fd2);
  15.    
  16. private:
  17.     SERIAL* s_a[MAX_SERIAL];
  18.     int counts;
  19.     pthread_t listen_td;
  20. };
LISTEN_SERIAL实现如下:

  1. #include "listen_serial.h"

  2. LISTEN_SERIAL::LISTEN_SERIAL():counts(0)
  3. {
  4.     for (int i = 0; i < 10; ++i)
  5.     {
  6.         s_a[i] = NULL;
  7.     }
  8. }

  9. LISTEN_SERIAL::~LISTEN_SERIAL()
  10. {
  11. }

  12. //开始监听串口线程
  13. void LISTEN_SERIAL::start_listen()
  14. {
  15.     pthread_create(&listen_td,NULL,listen_serial,this);
  16. }

  17. void* LISTEN_SERIAL::listen_serial(void* pointer)
  18. {
  19.     struct timeval timeout;
  20.     fd_set fdread;
  21.     LISTEN_SERIAL* pt = (LISTEN_SERIAL*)pointer;
  22.     if (pt->counts == 0)
  23.     {
  24.         return NULL;
  25.     }
  26.     while(!g_stop_listen_serial)
  27.     {
  28.         //set the param
  29.         FD_ZERO(&fdread);
  30.         for (int i = 0; i < pt->counts; ++i)
  31.         {
  32.             if(pt->s_a[i] != NULL)
  33.             {
  34.                 FD_SET(pt->s_a[i]->serial_fd,&fdread);
  35.             }
  36.         }
  37.         int maxfd = 0;
  38.         if (pt->s_a[0] != NULL)
  39.         {
  40.             maxfd = pt->s_a[0]->serial_fd;
  41.         }
  42.         for (int i = 1; i < pt->counts; ++i)
  43.         {
  44.             maxfd = pt->get_maxfd(maxfd,pt->s_a[i]->serial_fd);
  45.         }
  46.         timeout.tv_sec = 10;
  47.         timeout.tv_usec = 0;

  48.         //start select
  49.         int ret = select(maxfd+1,&fdread,NULL,NULL,&timeout);
  50.         if(ret < 0)
  51.         {
  52.             perror("select:");
  53.             continue;
  54.         }
  55.         else if(ret == 0)
  56.         {
  57.             continue;
  58.         }
  59.         //select OK,ret>0
  60.         for (int i = 0; i < pt->counts; ++i)
  61.         {
  62.             if(FD_ISSET(pt->s_a[i]->serial_fd,&fdread))
  63.             {
  64.                 char buf[1024];
  65.                 ret = read(pt->s_a[i]->serial_fd,buf,sizeof(buf));
  66.                 if(ret <= 0)
  67.                 {
  68.                     printf("串口%d已关闭", i);
  69.                     //do not listen again
  70.                     pt->s_a[i] = NULL;
  71.                 }
  72.                 else
  73.                 {
  74.                     //调用派生类中实现的deal_data来处理各自的数据
  75.                     pt->s_a[i]->deal_data(buf,ret);
  76.                     memset(buf,0,sizeof(buf));
  77.                 }
  78.             }
  79.         }//end of for
  80.     }//end of while
  81.     return NULL;
  82. }

  83. //把串口加入到监听队列
  84. void LISTEN_SERIAL::get_serial(SERIAL* s)
  85. {
  86.     s_a[counts++] = s;
  87. }

  88. //比较两个文件描述符,返回较大的一个
  89. int LISTEN_SERIAL::get_maxfd(int fd1,int fd2)
  90. {
  91.     return (fd1>fd2?fd1:fd2);
  92. }

上面我们构建了串口基类SERIAL,派生出两个串口类serial_1和serial_2。然后构建了监听串口的LISTEN_SERIAL类,现在我们用LISTEN_SERIAL类来监听这两个串口,然后让串口各自的deal_data来处理自己的数据。

  1. void main()
  2. {
  3.     //两个串口
  4.     serial_1 s1;
  5.     serial_2 s2;
  6.     //串口监听类
  7.     LISTEN_SERIAL ls;
  8.     
  9.     if(s1.open_serial("/dev/ttyXXX"))
  10.     {
  11.         s1.set_baudrate(9600);
  12.         s1.set_parity(8,1,'N');
  13.         //加入监听队列
  14.         ls.get_serial(&s1);
  15.     }
  16.     if(s2.open_serial("/dev/ttyXXX"))
  17.     {
  18.         s2.set_baudrate(57600);
  19.         s2.set_parity(8,1,'N');
  20.         //加入监听队列
  21.         ls.get_serial(&s2);
  22.     }

  23.     //开始监听
  24.     ls.start_listen();
  25.     //等待监听线程返回
  26.     ...
  27.     return;
  28. }
这样就在ls中只创建了一个线程来监听两个串口。如果还要增加串口,就按同样的方法派生串口类,然后在mian中加入监听队列就行了。

转载请注明出处:http://blog.chinaunix.net/uid-30008524-id-5575690.html

阅读(3440) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~