Interrupting Syscalls in Threads on Linux

Interrupting syscalls in threads on linux

You can signal a thread using pthread_kill(3).

The pthread_kill() function sends the signal sig to thread, another
thread in the same process as the caller.

If a signal handler is installed, the handler will be invoked in the
thread thread.

Note, you don't have to kill the thread; you can send a signal that simply makes accept fail with EINTR.

interruption of blocked I/O functions

How i can interrupt "read(...)" syscall from another thread ? Can i call "close(serial_port)" from another thread to interrupt all I/O blocking functions ? (i need to interrupt I/O blocking functions to close the thread correctly)

You can send a signal to the thread. That will interrupt the read function.

You absolutely cannot close the serial port from another thread. That can cause all kinds of disasters. Consider:

  1. This thread is about to call read but hasn't called it yet.
  2. Another thread calls close on the serial_port.
  3. Before the first thread can call read another thread accepts an incoming socket connection for a critical internal system purpose.
  4. That thread happens to get the same descriptor as serial_port.
  5. This thread calls read then fwrite, dumping the data received on the critical socket to stdout.

Good thing it's stdout. Because a bug once caused that exact same scenario -- except sensitive data was sent to random inbound connections from the Internet rather than stdout.

How to interrupt a thread performing a blocking socket connect?

Try to close() the socket to interrupt the connect(). I'm not sure, but I think it will work at least on Linux. Of course, be careful to synchronize properly such that you only ever close() this socket once, or a second close() could theoretically close an unrelated file descriptor that was just opened.

EDIT: shutdown() might be more appropriate because it does not actually close the socket.

Alternatively, you might want to take a look at pthread_cancel() and pthread_kill(). However, I don't see a way to use these two without a race condition.

I advise that you abandon the multithreaded-server approach and instead go event-driven, for example by using epoll for event notification. This way you can avoid all these very basic problems that become very hard with threads, like proper shutdown. You are free to at any time do anything you want, e.g. safely close sockets and never hear from them again.

On the other hand, if in your worker thread you do a non-blocking connect() and get notified via epoll_pwait() (or ppoll() or pselect(); note the p), you may be able to avoid race conditions associated with signals.

Internals of a Linux system call

A crash course in kernel mode in one stack overflow answer

Good questions! (Interview questions?)


  • What happens (in detail) when a
    thread makes a system call by raising
    interrupt 80?

The int $80 operation is vaguely like a function call. The CPU "takes a trap" and restarts at a known address in kernel mode, typically with a different MMU mode as well. The kernel will save many of the registers, though it doesn't have to save the registers that a program would not expect an ordinary function call to save.

  • What work does Linux do to the
    thread's stack and other state?

Typically an OS will save registers that the ABI promises not to change during procedure calls. The stack will stay the same; the kernel will run on a per-thread kernel stack rather than the per-thread user stack. Naturally some state will change, otherwise there would be no reason to do the system call.

  • What changes are done to the
    processor to put it into kernel mode?

This is usually entirely automatic. The CPU has, generically, a software-interrupt instruction that is a bit like a functional-call operation. It will cause the switch to kernel mode under controlled conditions. Typically, the CPU will change some sort of PSW protection bit, save the old PSW and PC, start at a well-known trap vector address, and may also switch to a different memory management protection and mapping arrangement.

  • After running the interrupt handler,
    how is control restored back to the
    calling process?

There will be some sort of "return from interrupt" or "return from trap" instruction, typically, that will act a bit like a complicated function-return instruction. Some RISC processors did very little automatically and required specific code to do the return and some CISC processors like x86 have (never-really-used) instructions that would execute dozens of operations documented in pages of architecture-manual pseudo-code for capability adjustments.

  • What if the system call can't be
    completed quickly: e.g. a read from
    disk. How does the interrupt handler
    relinquish control so that the
    processor can do other stuff while
    data is being loaded and how does it
    then obtain control again?

The kernel itself is threaded much like a threaded user program is. It just switches stacks (threads) and works on someone else's process for a while.

do system calls execute inside a software interrupt handler in entirety?

The syscalls run on most kernels inside an ISR. Take a quick glance at a former release of Linux and you will notice the int $Ox80 to invoke the kernel. This solution which is probably the simplest from a kernel development point of view, has a strong drawback: as long as running the ISR; interrupts are disabled. Disabling interrupts too long sucks because it's obvious your system won't be reactive (it delays external events, it doesn't reschedule on time, ...).

Preemption, as Adel explained in his answer is a smart solution. But whenever the kernel choose to preempt a thread because of an unavailable ressource, it has generally already spent a lot of time with interrupts disabled.

Are system calls offloaded to other threads?

You're right. Interrupt-threads and/or threaded kernel is an even smarter solution. Kernels like Solaris and Mac OS X prefers to have very simple ISRs which just wakeup high priority interrupt threads. Therefore the ISRs are reduced to the minimum processings, and the time the system runs with interrupts disabled is strongly decreased. Because these interrupt-threads have an high priority, they are likely to run at the return of the ISR. What is nice is interrupts will be enabled again, and therefore an even higher priority work wouldn't be delayed. With a threaded kernel, such as Linux in its recent releases, multiple things can be done inside the kernel, and despite one blocks, other process are still able to enter the kernel.

Hope this help!



Related Topics



Leave a reply



Submit