roov
roov

Reputation: 33

Why select function is not blocked in linux

I use select() to block the serial port. And I input a message to serial port, Then I receive like this. In my mind, I think it will print once, but in fact, it print continuously. So where do I do wrongly? serial port

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/shm.h>
#include <pthread.h>

int fd_se;
fd_set readfs;

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio,oldtio;
    if  ( tcgetattr( fd,&oldtio)  !=  0)
    {
        perror("SetupSerial 1");
        return -1;
    }
    bzero( &newtio, sizeof( newtio ) );
    newtio.c_cflag  |=  CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;

    switch( nBits )
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    }

    switch( nEvent )
    {
    case 'O':                     
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'E':                     
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'N':                   
        newtio.c_cflag &= ~PARENB;
        break;
    }

switch( nSpeed )
{
    case 2400:
        cfsetispeed(&newtio, B2400);
        cfsetospeed(&newtio, B2400);
        break;
    case 4800:
        cfsetispeed(&newtio, B4800);
        cfsetospeed(&newtio, B4800);
        break;
    case 9600:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    case 38400:
        cfsetispeed(&newtio, B38400);
        cfsetospeed(&newtio, B38400);
        break;
    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    default:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
}
if( nStop == 1 )
{
    newtio.c_cflag &=  ~CSTOPB;
}
else if ( nStop == 2 )
{
    newtio.c_cflag |=  CSTOPB;
}
newtio.c_cc[VTIME]  = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
    perror("com set error");
    return -1;
}
printf("set done!\n");
return 0;
}

int open_port(int i)
{
    char *dev[]={"/dev/ttySAC0","/dev/ttySAC1","/dev/ttySAC2","/dev/ttySAC3"};
    long vdisable;
    int fd;
    fd = open( dev[i], O_RDWR|O_NOCTTY|O_NDELAY);
    if (-1 == fd)
    {
        printf("Can't Open Serial Port %s\n",dev[i]);
        return(-1);
    }
    else
        printf("Open Serial Port %s success!\n",dev[i]);
    if(fcntl(fd, F_SETFL, 0)<0)
    {
        printf("fcntl failed!\n");
        exit(0);
    }
    else
         printf("fcntl id is %d\n",fcntl(fd, F_SETFL,0));
    if(isatty(STDIN_FILENO)==0)
    {
        printf("standard input is not a terminal device\n");
        exit(0);
    }
    else
        printf("This is a terminal device\n");
        return fd;
    }

void *serial_function(void *args)
{



    while(1)
    {
        FD_SET(fd_se, &readfs);  /* set testing for source 1 */
        select(fd_se+1, &readfs, NULL, NULL, NULL);

        if (FD_ISSET(fd_se,&readfs))
        {
            printf("serial_function is running\n");

        }   
    }
       close(fd_se);
}

int main(void)
{
    int i;
    if((fd_se=open_port(1))<0)
    {
        perror("open_port error");
        return;
    }
    if((i=set_opt(fd_se,38400,8,'N',1))<0)
    {
        perror("set_opt error");
        return;
    }

    pthread_t thread1;
    if(pthread_create(&thread1, NULL, serial_function, NULL) != 0)
    {
         perror("Thread create failed!");
         exit(EXIT_FAILURE);
     }
    while(1)
    {
        sleep(1);
    }
    return;
}

Upvotes: 0

Views: 372

Answers (3)

joop
joop

Reputation: 4503

You need to check the return value you get from select. The fd_sets contain valid bitmasks only if this return value is greater than zero. Otherwise, they are undefined (but probably unchanged in linux). The return from select() indicates:

  • -1 : error condition. But beware: not all conditions are errors: EAGAIN and EALARM indicate the occurence of an event other than a status change in one of the fd_sets. For network sockets, there are also some other non-fatalresource_temporarily_unavailable conditions possible.
  • 0 : timeout : the time given in the last argument to select() is exceeded (this cannot happen in this particular case because the last argument is NULL)
  • >= 1 one or more of the file descriptors are readable or writeable. (this means: you can read() or write() them without blocking) Check the bits in the fd_sets to find out which fds to read/write

#include <errno.h>

while(1)
{
    int rc;
    char buff[16];

    FD_ZERO(&readfs);
    FD_SET(fd_se, &readfs);  /* set testing for source 1 */
    rc = select(fd_se+1, &readfs, NULL, NULL, NULL);
    if (rc == -1) switch (errno) {
        default:
          fprintf(stderr,"Err=%d\n", errno);
        case EAGAIN: // EINTR
          continue; // try again ...
        }
    if (rc == 0) {
        fprintf(stderr,"Timeout\n");
        continue;
        }
        /* rc must be >= 1, now ... */
    if (FD_ISSET(fd_se,&readfs))
    {
        printf("serial_function is running\n");
        rc = read(fd_se, buff, sizeof buff);
                /* handle EOF */
        if (!rc) break;
                /* handle error */
        if (rc < 0) break;
        // ... do something useful with the received data
    }
}

Upvotes: 1

unwind
unwind

Reputation: 399843

Well, you're notified that that file has data ready for reading, but then you don't read that. Of course, the next time you ask, the file still has data ready for reading.

The select() function doesn't track the actual data to know if it's already notified for it or not. As long as the file is readable, it will report as being readable.

To clear it, read out the data.

Also, as pointed out in another answer, you should clear the file descriptor set before using it.

Upvotes: 2

LPs
LPs

Reputation: 16223

You must reset to zero your set each loop

while(1)
{
    FD_ZERO(&readfs);
    FD_SET(fd_se, &readfs);  /* set testing for source 1 */
    select(fd_se+1, &readfs, NULL, NULL, NULL);

    if (FD_ISSET(fd_se,&readfs))
    {
        printf("serial_function is running\n");

    }   
}

Moreover you must read data available to make the FD not "ready to be read"

Upvotes: 1

Related Questions