阻塞和非阻塞(串口自环测试失败原因定位)

串口自环测试失败原因定位

INT32 serial_test()
{
    INT32 fd;
    INT32 i;
    char rbuf[1];
    char wrbuf[1];
    INT32 count = 0;
    INT32 cnt = 0;
    rbuf[0] = 0;
    int ret;
 //VCS_Ctrl_LedConf(SC1_LED,RED,FAST_BLINK);  //SC4红灯灭
// printf("serial_test::11111\n");
 
    printflag = 1;
    fd = open( "/dev/ttyS0", O_RDWR| O_NOCTTY  | O_NONBLOCK);
    if (fd < 0)
    {
        DPRINTF(PRINT_FLAG_ERR,"can't open ttyS0\n");
        close(fd);
    }
    else
     {
        DPRINTF(PRINT_FLAG_ERR,"open OK...\n");
    }
// printf("serial_test::22222\n");  
 tcflush(fd,TCIOFLUSH);
 for(i=0;i<10000;i++)
 {
    rbuf[0] = 0;
    read(fd,rbuf,1);
 }
//printf("serial_test::3333\n");
// VCS_Ctrl_LedConf(SC3_LED,RED,FAST_BLINK);  //SC4红灯灭
 if(0 == tty_raw(fd))
 DPRINTF(PRINT_FLAG_ERR,"tty raw set OK \n");
// VCS_Ctrl_LedConf(SC3_LED,GREEN,FAST_BLINK);  //SC4红灯灭
 
 #if 0   
 {
       
   INT32 timeOut = 0;
        /* hope to read 10 byte from serial com0*/
        while(1)
        {
            ret = read(fd, rbuf, 1000);
            if(ret <= 0)
            {
                timeOut ++;
                if (timeOut > 10)
                    break;
            }
        }
    }
 #endif
    while(1)
    {
        tcflush(fd,TCIOFLUSH);
       
        wrbuf[0] = 'u';
        rbuf[0] = 0;

        ret=write(fd,wrbuf,1);
        if(ret== -1)
        {
            printf("write error!\n");
            perror("wirte failed");
        }
        if(ret==1)
            printf("write success!");
        usleep(1000);  //lixia add 2010-06-13
        
        ret = 0;
    cnt = 0;
        while(ret != 1)

 {
            cnt++;
            ret=read(fd,rbuf,1);
            if(ret== -1)
            {
                 printf("read error! 0x%x\n",errno); 这里发现经常读错误,而且返回的errno为0xb,
                 perror("read failed");
            }
            if(ret==1)
                printf("read success! cnt=%d\n",cnt);
             usleep(1000);
            if(cnt>2000)
                break;
  }


        printf("w:%c %d r:%c %d \n",wrbuf[0],wrbuf[0],rbuf[0],rbuf[0]);
        if(wrbuf[0] == rbuf[0])
        {
            Beep_Warning_On();
            sleep(1);
            Beep_Warning_Off();
            sleep(1);
           if (0 == tty_reset(fd))
                DPRINTF(PRINT_FLAG_ERR,"tty set RESET ok\n");
          // sleep(2);
            close(fd);
            printflag = 0;
          //   VCS_Ctrl_LedConf(SC1_LED,GREEN,FAST_BLINK);  //SC4红灯灭
            return 0;
        }
        else
        {
           count ++;
           if(count > 1000)
           {
              if (0 == tty_reset(fd))
              {
                  DPRINTF(PRINT_FLAG_ERR,"tty set RESET ok\n");
              }
                close(fd);
                printflag = 0;
                 VCS_Ctrl_LedConf(SC1_LED,GREEN,FAST_BLINK);  //SC4红灯灭
                return -1;
           }
        }
    }    
}

发现“标红的read函数”经常会读错误,而且返回的errno为0xb,

0xb对应EAGAIN。

因为open函数使用的是O_NONBLOCK,非阻塞,所以定位到原因,因为是非阻塞模式,所以如果没有数据时read会直接返回失败,导致写入不等于读取的数据,如果超过1000次,就判断串口自环测试失败。优化后的代码见上图compare。

知识点:一定要判断函数的返回值!!!

--------------------------------------------------------------------------------------------------------------------------------------

非阻塞(O_NONBLOCK)

非阻塞I/O使我们的操作要么成功,要么立即返回错误,不被阻塞。
对于一个给定的描述符两种方法对其指定非阻塞I/O:
(1)调用open获得描述符,并指定O_NONBLOCK标志
(2)对已经打开的文件描述符,调用fcntl,打开O_NONBLOCK文件状态标志。

flags = fcntl( s, F_GETFL, 0 ) )
fcntl( s, F_SETFL, flags | O_NONBLOCK )

--------------------------------------------------------------------------------------------------------------------------------------

Linux:非阻塞O_NONBLOCK与O_NDELAY的区别

O_NONBLOCK和O_NDELAY所产生的结果都是使I/O变成非阻塞模式(non-blocking),在读取不到数据或是写入缓冲区已满会马上return,而不会阻塞等待。

它们的差别在于:在读操作时,如果读不到数据,O_NDELAY会使I/O函数马上返回0,但这又衍生出一个问题,因为读取到文件末尾(EOF)时返回的也是0,这样无法区分是哪种情况。因此,O_NONBLOCK就产生出来,它在读取不到数据时会回传-1,并且设置errno为EAGAIN

O_NDELAY是在System V的早期版本中引入的,在编码时,还是推荐POSIX规定的O_NONBLOCK,O_NONBLOCK可以在openfcntl时设置。

--------------------------------------------------------------------------------------------------------------------------------------

Linux 设备驱动--- 阻塞型字符设备驱动 --- O_NONBLOCK --- 非阻塞标志

阻塞:

          在设计简单字符驱动程序时,要注意一个重要问题.

          当一个设备无法立刻满足用户的读写请求时应当如何处理?

          例如:调用 read 时没有数据可读,但以后可能会有;

          或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据.

          应用程序通常不关心这种问题,应用程序只是调用 read 或 write 并得到返回值.

          驱动程序应当 ( 缺省地 ) 阻塞进程使它进入睡眠直到请求可以得到满足.

阻塞操作:

          是指在执行设备操作时,若不能获得资源挂起进程,直到满足可操作的条件后进行操作,

          被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足.

非阻塞操作:

          进程不能进行设备操作时并不挂起,他或者放弃或者不停的查询,直到可以进行操作为止.

阻塞方式-read- 实现:

          在阻塞型驱动程序中,read 实现方式如下:

          如果进程调用 read ,但设备 没有数据 或 数据不足,进程阻塞.

          当新数据到达后唤醒被阻塞进程.

阻塞方式-write- 实现:

          在阻塞型驱动程序中,write 实现方式如下:

          如果进程调用了 write ,但设备 没有足够的空间供其写入数据, 进程阻塞.

          当设备中的数据被读走后,缓冲区中 空出部分空间,则 唤醒进程.

非阻塞方式的读写操作:

          阻塞方式是文件读写操作的 默认方式,但是应用程序员可通过使用 O_NONBLOCK 标志来人为

          的 设置读写操作为 非阻塞方式 .( 该标志定义在 < linux/fcntl.h > 中,在打开文件时指定 ) .

          如果 设置了 O_NONBLOCK 标志,read 和 write 的行为是不同的 ,如果进程没有数据就绪时调用了 read ,

          或者在缓冲区没有空间时调用了 write ,系统只是 简单的返回 -EAGAIN,而 不会阻塞进程.

实例 --- 读阻塞的实现:

          

          

用 while 是因为可能别的信号唤醒了睡眠,我们要通过while 重新检测是否真有数据了....

          

实例 --- 按键驱动阻塞实现:

1,在 open 函数 查看看是 阻塞方式 还是 非阻塞方式:

         file 结构体中含有 f_flags 标志位,看是 阻塞方式 还是 非阻塞方式:

          O_NONBLOCK 为 非阻塞方式

	if (file->f_flags & O_NONBLOCK)  /* 非 阻塞操作 */
	{
		if (down_trylock(&button_lock))   /* 无法获取信号量,down_trylock 立马返回 一个 非零值 */
			return -EBUSY;
	}
	else                             /* 阻塞操作 */
	{
		/* 获取信号量 */
		down(&button_lock);   /* 获取不到  睡眠 */
	}

2,在 read 函数中同样查看:

	if (file->f_flags & O_NONBLOCK)       /* 非 阻塞操作 */
	{
		if (!ev_press)                 /* ev_press 为 1 表示有按键按下,为 0 if 成立 ,没有按键按下, */
			return -EAGAIN;        /* 返回 -EAGAIN 让再次来执行 */
	}
	else                                   /* 阻塞操作 */
	{
		/* 如果没有按键动作, 休眠 */
		wait_event_interruptible(button_waitq, ev_press);
	}
<table><tr><td bgcolor=#7FFFD4>这里的背景色是:Aquamarine,  十六进制颜色值:#7FFFD4, rgb(127, 255, 212)</td></tr></table>

3,应用程序中:

1,以阻塞方式运行:

后台执行应用程序,进程处于睡眠状态,按下按键,立马打印按键号;

int main(int argc, char **argv)
{
	unsigned char key_val;
	int Oflags;
                                                   
	fd = open("/dev/buttons", O_RDWR );
	if (fd < 0)
	{
		printf("can't open!\n");
		return -1;
	}
 
	while (1)
	{
		read(fd, &key_val, 1);
		printf("key_val: 0x%x\n", key_val);
	}
	
	return 0;
}


 

2,以非阻塞方式运行:

open 驱动程序的时候,传入标志 O_NONBLOCK 非阻塞;

后台执行应用程序:

int main(int argc, char **argv)
{
	unsigned char key_val;
	int ret;
	int Oflags;
 
	fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
	if (fd < 0)
	{
		printf("can't open!\n");
		return -1;
	}
 
	while (1)
	{
		ret = read(fd, &key_val, 1);
		printf("key_val: 0x%x, ret = %d\n", key_val, ret);
		sleep(5);
	}
	
	return 0;
}

 

非阻塞方式,没有按键值按下,程序立马返回;
read 返回值 为 -1;

https://blog.csdn.net/yikai2009/article/details/8653697#t7

猜你喜欢

转载自blog.csdn.net/u014426028/article/details/112789104