select问题!多任务系统下,ARM与单片机串口通讯任务,为什么用select阻塞串口通讯任务后,任务就不能恢复了?

bfy520   2008-1-12 13:22 楼主
该串口初始化如下
ioctl(comm2Fd,FIOBAUDRATE,9600)
ioctl(comm2Fd,FIOSETOPTIONS,OPT_RAW)

使用如下

        FD_ZERO (&readFds);
        FD_SET (comm2Fd, &readFds);
        width = comm2Fd + 1;
        FD_ISSET (comm2Fd, &readFds);
       
        FOREVER
        {
                if(timeoutvalue==0)
                {
                        printf("\nselect start!\n");
                        selectnum = select (width, &readFds, NULL, NULL, NULL);
                        printf("\nselect over!\n");
                }

                ...........
        }

现在的状况是程序跑一段时间后会死机或这个串口通讯任务死掉,每次死机都是“select start!”打印出来,而“select over!”打印不出来,在仅这个串口通讯任务死掉的情况下,用comm1Fd超级终端登陆,查询任务状态,会发现tExcTask任务居然处于挂起状态???
哪位大哥帮忙分析一下或给予一点提示,小弟不胜感激!!

回复评论 (15)

专业提供SDK二次开发包的短信平台接口等,企业短信群发平台,可群发短信、彩信、语音、传真、邮件等。发送接受速度快,成功率高,5万家用户保证质量!附注:软件界面简洁化,操作简便化,不需要任何硬件配备(除了电脑及上网)可以几个部门同时使用,并可以由一个总帐号管理,可以随时查看子帐号的使用情报况,余额分配等有如意通客户端,网络版,SDK短信平台接口等多版本供您选择

联系人:刘斌   手机号码:13163799460   QQ:178493093   MSN:liubinkyt@hotmail.com   
点赞  2008-1-12 16:29
select函数在设备读(或写)操作未就绪时应该处于阻塞状态,也就是说你的进程在串口没有收到数据时会处于挂起状态,一旦select监测到有数据可读(或可写如),进程就会转为运行态,继续执行。

从你描述的现象上看,进程本身因该没有当掉,而是底层驱动没有收到数据供应用层读取。不知道你用的是CPU自带串口还是扩展串口。建议你查查串口驱动,有可能的话把中断信号牵出来看看,是不是每次中断都能正确响应。
点赞  2008-1-14 23:43

4楼 ojo 

avbsp您好,我还有一个问题,我上面给出的代码是不是有些问题?
FD_ZERO   (&readFds);
FD_SET   (comm2Fd,   &readFds);  这两句是不是应该写在FOREVER 循环中?
点赞  2008-1-17 15:00
是的。每次都清除
点赞  2008-1-17 16:04
请问,如果不清除,按照我当前的代码来写,会有什么问题吗?能解释一下吗?谢谢
点赞  2008-1-17 16:28
详细函数如下:
static STATUS RecvDataWithTimeOut(UINT16 datacount,UINT16 timeoutvalue)
{
       ....
       ....
       
        timeout.tv_sec = timeoutvalue;
        timeout.tv_usec = 0;       
        FD_ZERO (&readFds);
        FD_SET (comm2Fd, &readFds);
        width = comm2Fd + 1;
        FD_ISSET (comm2Fd, &readFds);
       
        FOREVER
        {
                if(timeoutvalue==0)
                        selectnum = select (width, &readFds, NULL, NULL, NULL);
                else
                        selectnum = select (width, &readFds, NULL, NULL, &timeout);
               
                if (selectnum == ERROR)
                {
                  #ifdef DEBUG_PRINT
                    printErr("ERROR:An select error has occurred in received data from COM2!\n");
                  #endif
                  return ERROR;
                }
                else if(selectnum == 0)
                {
                   #ifdef DEBUG_PRINT
                    printErr("ERROR:An timeout error has occurred in received data from COM2!\n");
                   #endif
                   return ERROR;
                }
               
                if (FD_ISSET (comm2Fd, &readFds))
                {
                  bytesRead = fioRead(comm2Fd,szBuf,datacount);                       
                  return bytesRead;
                }
        }
       
}
我的问题是,因为我用的是fioRead,所以还用不用将
FD_ZERO (&readFds);   
FD_SET (comm2Fd,&readFds);这两句写在FOREVER循环中?
如果需要写入,为什么?
如果不写入按照我当前的代码写,会出现什么问题?
还有这里用fioRead代替read会出现什么问题吗?
点赞  2008-1-17 16:45
我使用read函数来读数据,select的使用方式和你差不多,不过作了一下保护:
void UartRX(void)
{
    struct fd_set ltv_readFds;
    struct fd_set ltv_saveFds;

    int liv_width;
    int liv_numFds;

    liv_width = 0;
    FD_ZERO(<v_saveFds);    /* initialize the set - all bits off */

    /*
     *  The liv_width argument is the maximum file descriptor to
     *  be tested, plus one. The descriptors 0,1, up through
     *  and including liv_width -1 are tested.
     */
    FD_SET(UartInfo.fd, <v_saveFds);  /* Turn on bit for panel uart fd */
    liv_width = UartInfo.fd;
    liv_width++;

    FOREVER
    {
        ltv_readFds = ltv_saveFds;
        /*
         *  Pend on multiple file descriptors indefinitely using select
         *  until one or more file descriptors become ready for reading.
         */
        if ((liv_numFds = select(liv_width, <v_readFds, NULL, NULL, NULL)) == ERROR)
        {
            logMsg("ERROR in select.\n");

            continue;
        }

        if (FD_ISSET(UartInfo.fd, <v_readFds))
        {
            // read data from uart
        }
    }

    return;
}

问个问题:如果你的函数在select错误或超时后退出,该函数还会重新创建吗?
点赞  2008-1-18 23:39
不会了!我的函数比较弱!select错误或超时后退出就丢弃这次通讯,这样除了丢失信息还会有其他问题吗?
点赞  2008-1-20 10:35
为什么我在我的代码里把fioRead换成read就不行,是不是要对收的字节数进行判断循环接收,read不是读取制定的字节数吗?
点赞  2008-1-20 11:52
通常情况下,如果你需要一直监测串口,最好让串口接收进程始终处于运行状态,在select错误或超时后给出提示信息并让循环重新执行;要么在接收串口数据前发起进程,前提是你知道数据什么时候来。

对于read函数,你指定的dataCount是要读取的最大长度。假设缓冲区中有10个字节,你指定的dataCount为5,那么
read(fd, buf, dataCount)的返回值为5;如果缓冲区中只有3个字节,dataCount仍为5,那么read(fd, buf, dataCount)将返回实际读取的字节数——3

你可以根据需要定义一套通信协议,按照一定的格式进行通信,如
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| 起始标志(1字节) | 长度(1字节) | 数据........(n字节) | 结束表示(1字节) |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
根据指定的数据帧结构就可以判断数据是否接收完毕。
点赞  2008-1-21 23:44
这个任务在运行的一段后会打印(前后调用select均正常)
select start!

Undefined instruction
Exception address: 0x0005c1e0
Current Processor Status Register: 0x20000013
Task: 0xff45c8 "tTffsPTask"
select over!

请问是怎么回事?我仔细看了一下这个文件没有include库文件selectLib.h,是这个原因吗?这个原因会造成死机吗?
点赞  2008-1-24 22:25
FD_ZERO   (&readFds);      
FD_SET   (comm2Fd,&readFds);这两句要写在FOREVER循环中。

你可以自己监测一下,每次select完,readFds都被改动了,所以必须重新设置。不设置,会导致select不按照你预想的对comm2Fd进行IO检查,这样即使你comm2有数据,也无法激活select
点赞  2008-2-29 08:43
那个"tTffsPTask"任务是调用你那个select程序的任务吗?
确认一下comm2Fd是不是打开的串口2对应的fd。
其实如果对select函数不熟悉的话,看看示例代码,或者如果赶时间的话直接别用select了。某个地方自己没有搞清楚,觉得有也许可能没有问题的时候,往往就会出问题。

可以专门启动一个任务直接对串口read好了,将获取的数据往一个消息队列里放,然后其他任务从消息队列里读就好了(或者将这个过程封装成读函数就更好了),消息队列反正是有timeout功能的了。这样就能实现想要的功能而不用select了。
点赞  2008-3-2 23:04
我也想知道,正在找這方面的資料~~~~~
点赞  2008-4-30 20:12
不知,帮顶
点赞  2008-5-1 06:00
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复