> When I substitute the mouse device, it sees and prints the mouse clicks, but when I send it a "kill -INT ????",
> it prints "INT!!!" but doesn't terminate until it sees another mouse click, printing one mouse click and then terminating.
There is absolutely nothing strange about that.
On your system (and mine as it happens), the simple signal() interface sets SA_RESTART flag, which causes the read() to resume and deliver just one more result before you get around to testing the global state flag g_continue.
You can see this if you strace the process to find out how things work at the OS interface.
Here, the -DTHE_OLD_WAY compiles the version of main which uses signal() in my post #7
Code:
$ gcc -DTHE_OLD_WAY foo.c
$ strace -e trace=file,signal ./a.out
execve("./a.out", ["./a.out"], [/* 65 vars */]) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
rt_sigaction(SIGINT, {0x4007e6, [INT], SA_RESTORER|SA_RESTART, 0x7f209613b4b0}, {SIG_DFL, [], 0}, 8) = 0
hello
Read 6 bytes=hello
wor --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=4097, si_uid=1000} ---
INT!!!
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
usleep errno=4, msg=Interrupted system call
Terminated
Because I've set up the descriptor to be non-blocking, 99% of the time is spent in usleep(), and it's that function which gets interrupted by the signal handler (usleep errno=4), which is then followed by testing the flag and the inevitable exit of the loop.
It's also worth noting at this stage that read() is one of those functions which is restarted if the signal disposition is SA_RESTART, whereas usleep() isn't.
If I comment out the fcntl call to make the descriptor blocking once more, I observe
Code:
$ strace -e trace=file,signal ./a.out
execve("./a.out", ["./a.out"], [/* 65 vars */]) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
rt_sigaction(SIGINT, {0x400796, [INT], SA_RESTORER|SA_RESTART, 0x7fab6da334b0}, {SIG_DFL, [], 0}, 8) = 0
hello
Read 6 bytes=hello
wor --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=4036, si_uid=1000} ---
INT!!!
rt_sigreturn({mask=[]}) = 0
Read 6 bytes=wor
Terminated
Because there is SA_RESTART, the read() call is NOT abandoned and errno set to 4, so the program still hangs around for the read() to complete it's transaction, and return a meaningful buffer to main(). But since the signal hander happened, the global flag is set and the loop exits as per normal.
With the sigaction code, there is no SA_RESTART flag!
Code:
$ gcc foo.c
$ strace -e trace=file,signal ./a.out
execve("./a.out", ["./a.out"], [/* 65 vars */]) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
rt_sigaction(SIGINT, {0x400756, [], SA_RESTORER, 0x7fa5660f74b0}, {SIG_DFL, [], 0}, 8) = 0
hello
Read 6 bytes=hello
wor --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=4081, si_uid=1000} ---
INT!!!
rt_sigreturn({mask=[]}) = -1 EINTR (Interrupted system call)
Read errno=4, msg=Interrupted system call
Terminated
In this case, it's the read() call itself which returns immediately with Read errno=4.