How to change Linux interrupt timer for tcsetattr()

淺唱寂寞╮ 提交于 2019-12-25 05:31:34

问题


I am using serial port, and while communicating with this, i have to change the configuration using tcsetattr() with TCSDRAIN mode.

TCSADRAIN
    The change should take place after all output written to fd has been read by the master pseudoterminal. Use this value when changing terminal attributes that affect output.

And while calling tcsetattr() with TCSDRAIN, if there still remain output data in buffer, Linux seems blocked and check the buffer again after a some interrupt time to change a configuration.

I tested for this like below

  • First Normall Case

    1. write data to serial
    2. change configuration using tcsetattr()
    3. there is a remaining data in output buffer
    4. the process blocked during regular interval, for example 20ms
    5. wake up.
  • Second Case

    1. write data to serial
    2. take sleep() manually for 5ms, it means give enough time to clear output to Linux
    3. there is no remaining data
    4. change configuration using tcsetattr()
    5. there is no block

And for me the interrupt time is too long to do what i want

How can i change this interrupt timer interval? (in Ubuntu and raspberrypi)


回答1:


I do not think that the .cc[VTIME] field of the termios structure affects the flush timeout, if that's what you are asking. As far as I know, it only affects the read() behaviour.

However, there are several ways you can control the interval the kernel tries to drain/flush, before giving up.

One option is to use fcntl(fd, F_SETFL, O_NONBLOCK) to (temporarily) set the descriptor to non-blocking state, and use clock_gettime(CLOCK_MONOTONIC, &now) and nanosleep() to retry tcsetattr(fd, TCSADRAIN, &attrs) a few times over a suitable time interval, until it succeeds. Then revert the descriptor to normal mode using fcntl(fd, F_SETFL, 0). If the tcsetattr() calls failed with errno == EWOULDBLOCK || errno == EAGAIN, use tcsetattr(fd, TCSANOW, &attrs) to discard all unread/unsent data.

Note that I have not tested the above on RPi! It might not work on some specific architectures/serial port drivers, as it is possible to hardcode the flush/drain interval into the driver and disregard the fact that the descriptor is in non-blocking mode. That would be a bug, however, and is fixable with a kernel patch. (In other words, this should work, but some serial port drivers may be crappy, and ignore the nonblocking nature above, and will block until some unspecified interval.)

Another option is to use a timer to raise a signal. If you install a function to handle that signal without the SA_RESTART flag, the delivery of the signal will interrupt the blocking tcdrain()/tcflush()/tcsetattr() call. (If the process uses multiple threads, the signal should be blocked in all other threads, as otherwise the kernel will just pick one of the process' threads to deliver the signal. If it is not the thread in a blocking call, the blocking call will not be interrupted.)

Because signals are a very reliable method to interrupt most syscalls and library functions (see section Interruption of system calls and library functions by signal handlers in man 7 signal -- termios functions are equivalent to ioctl() calls in Linux), I personally prefer to have a separate thread do nothing but maintain these timeouts. Such a thread is very lightweight, and does not consume CPU time unless a timeout elapses or is added. I can also have multiple concurrent timeouts, with a simple interface to check, cancel, and add timeouts, which makes it easier to design the rest of the application to be robust and efficient.



来源:https://stackoverflow.com/questions/29293227/how-to-change-linux-interrupt-timer-for-tcsetattr

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!