Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic

Handling I/O Signals

by stephane (Monk)
on Mar 13, 2002 at 20:01 UTC ( #151509=perlquestion: print w/replies, xml ) Need Help??

stephane has asked for the wisdom of the Perl Monks concerning the following question:

Hi All,

Currently I have a problem catching (or handling) SIGIOs in a Perl script. What I would like to achieve, is to have a small script reading and writing to a serial port whithout the need to poll it every now and then to see if there is indeed something to read.

The code here under opens a serial port, uses POSIX and Fcntl to configure various parameters (baud rate, etc.) and to put it in asynchronous mode.

A SIGIO handler is set a the beginning of the script, which right now just prints out a string when an IO signal has been caught. sigtrap is there just in case other type of signals are sent (apperently not the case anyway).

Once all that is done ;-) it will write something on the line and wait for the signal(s) - I verified that the device indeed answers ("OK" in this case).

The problem is: no SIGIOs are sent back..

use POSIX; use Fcntl; use sigtrap qw(die untrapped); sub SIGIO_Handler { my $signame = shift; print "Somebody sent me a SIG$signame\n"; } $SIG{IO} = \&SIGIO_Handler; $| = 1; open(LINK, "+</dev/ttyS0") || die "Can't connet to the device: $!"; my $termios = new POSIX::Termios; $termios->getattr( fileno(LINK) ); my $c_cflag = $termios->getcflag; my $c_lflag = $termios->getlflag; $c_cflag |= (CLOCAL | CREAD | CS8); $c_cflag &= ~(PARENB); $c_cflag &= ~(CSTOPB); $c_cflag |= (CSIZE); $c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); $c_oflag &= ~(OPOST); $termios->setcc(VMIN, 0); $termios->setcc(VTIME, 10); $termios->setcflag($c_cflag); $termios->setlflag($c_lflag); $termios->setoflag($c_oflag); $termios->setispeed(B2400); $termios->setospeed(B2400); $termios->setattr( fileno(LINK), TCSAFLUSH); # -- Register "myself" ($$) to catch SIGIOs. fcntl(LINK, &Fcntl::F_SETOWN, $$); fcntl(LINK, F_SETFL, O_ASYNC); print LINK "AT\r"; while (1) { # -- Just wait and ... # -- see if any SIGIO arrives };

More precisely, if the script just waits (as it does in the above code) no SIGIOs will be catched. Instead, if (at some point) I start reading what is on the port (meaning: modify the script to actualkly read something at some point).. then the SIGIO_Handler will be used.

So, I dont want the signals to tell me that I am reading on the port.. I want them to tell me that there is something to read in the first place! (before I actually read it "myself").

Can somebody provide any hints/advice?
How can properly catch SIGIOs?

P.S.: by the way, I don't want to use the Device::SerialPort since I have the feeling that it should be possible through POSIX and Fcntl and they are in the standard distribution, or even Win32::SerialPort for the allready given reasons, plus.. Im on Linux (kernel v2.2)

Replies are listed 'Best First'.
Re: Handling I/O Signals
by count0 (Friar) on Mar 13, 2002 at 21:45 UTC
    How can properly catch SIGIOs?

    For the most part, the answer is: You can't - at least not reliably, portably, or effectively.

    Firstly, SIGIO is highly system specific. IIRC it is SVR and/or BSD - but definitely *not* POSIX.
    Secondly, SIGIOs are only sent for certain types of descriptors.. and to be honest, I'm not sure which ones (most probably terminals).

    Generally, it is far more preferred to use IO multiplexing. If you haven't already, check out IO::Select, select, and the examples in the Cookbook.
Re: Handling I/O Signals
by traveler (Parson) on Mar 13, 2002 at 22:04 UTC
    Some suggestions
    • use FASYNC instead of O_ASYNC; they are not always the same
    • Don't use an infitite loop! It can consume all the CPU time. Instead use sleep or better the system's pause system call via syscall()
    • set the line to O_NOCTTY | O_NONBLOCK
    • check out The Linux Serial Programming HOWTO for C examples
    HTH, --traveler

      Thanks a lot for all these remarks! Here under I try to address each of the suggestions that have been made:

      • used FASYNC instead of O_ASYNC: no change (they are, as far as I can see synomins anyway on my Linux system.
      • set the line to O_NOCTTY|O_NONBLOCK:could'nt see any change
      • use select. May be I dont understand this properly, but I cant see why it would help: using select would still require the script to constantly poll the port for any new input (?).

      Otherwise concerning the while loop.. it would be nice to use pause but since I cannot catch the signal in the first place.. I wanted to make the script as straight forward as possible (focus on the SIGIO and nothing else)

      Any further ideas/recommendations? Lets say I wan to dial into my computer, and I want a small perl script to handle this: do I have to constantly poll the Serial Port for input from the modem (every 5 or 10 secs.) and "suck" the system ressources (whith a "while loop" ;-)? Or is there another way of doing this in Perl?

        I suggested pause because the while loop may be keeping you from getting the signal in a reasonable amount of time. Someone who knows more perlguts may be able to correct me, but I have had this problem with C under various systems.

        select does not require constant polling. It hangs the program waiting for input (or output or a timeout); it thus replaces the while and the need for SIGIO. Do man select on your Linux box. Note that there are two selects in Perl.

        Also did you check the HOWTO ref I included?

        HTH, --traveler

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://151509]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (6)
As of 2022-08-19 08:37 GMT
Find Nodes?
    Voting Booth?

    No recent polls found