Re: how do threads work?

From: Enrique Perez-Terron (enrio_at_online.no)
Date: 11/20/05

  • Next message: Enrique Perez-Terron: "Re: how do threads work?"
    Date: Sun, 20 Nov 2005 01:15:36 +0100
    
    

    On Sat, 19 Nov 2005 23:03:31 +0100, Andersen <andersen_800@hotmail.com> wrote:

    >
    > Enrique Perez-Terron wrote:
    [...]
    >> A process cannot consider to switch task unless the flow of instructions
    >> brings it to a function that does such deliberations. While in the middle
    >> of computing pi to one million decimals, the flow of instructions does
    >> not get near any such function. To solve this, the process can ask the
    >> kernel for a little help, in the form of regular timer signals.
    >
    > Right. Though I am curious how that would be implemented on a IA32 arch
    > (Interrupts?).

    In what way is this different for a IA32 arch processor? What other kind
    of processors would you compare with?

    At the level where the processor per se is visible, you have external chips
    deliver interrupts to the processor, and all such interrupts invoke interrupt
    handlers that run in kernel mode. All processors I know about invoke
    interrupt handlers in whatever protection mode or ring level, or whatever
    it's called, that is used by the kernel, and implies hardware access etc.

    Remember, on a uniprocessor, when the kernel starts a process, the processor
    starts executing instructions from that process' code, and the processor
    by itself has no other means of being intelligent about what it is doing
    than by executing instructions that are intelligently composed.

    So, while the processor is executing the process' code, it has no other
    intelligence than that which is implicit in that process. The kernel, at
    that point in time is not a live being that can decide and act, it is just
    a bunch of instructions lying idle in the memory, about like the letters in
    a book in a shelf. Those instructions are powerless to look at the watch
    every thousandth of a second, as long as those instructions are not
    executed.

    This is where the interrupt comes in. The processor is built that way, it
    will not fetch and run the next instruction of the running program, it
    will instead fetch and run instructions from an interrupt handler whose
    address is found through a hardwired procedure. In this way, the kernel
    regains control over the computer every while, thanks to the regular
    interrupts from a timer circuit.

    In order for a user-level library like a user-level threads library to
    get notification at times when the thread (and therefore, the process) is
    doing the proverbial computation of pi decimals, i.e., when the flow of
    instructions does not naturally lead to functions in the thread library
    (which could poll the os to learn how long the pi decimals have been
    computing) -- you need something coming in from outside the process itself,
    and that is the signal, which the kernel can deliver, prompted by a
    hardware interrupt from an external (or internal, for that matter) circuit.

    If you now ask, how does the kernel deliver signals, then we are no longer
    talking about threads, but about the kernel and its processes (or tasks,
    or kernel-level threads) in general. Whenever the kernel is running some
    of its code, and is about to resume the active process, it checks the state
    of the signal bits in the process (er, task) structure, and if one is set,
    it invokes the process signal handler instead of the normal resumption
    point - after arranging whatever messy details are required so the normal
    resumption point can be resumed later, after the signal handler returns.

    >> Signals do force an interruption of the flow of instructions. Once in
    >> the signal handler, the threading library takes the steps needed to
    >> "preempt" the computation of pi in favor of something else.
    >>
    >> One of the technical hurdles that such a threading library must overcome,
    >> is to arrange for each "thread" to have a separate stack. It must be
    >> posible
    >> for one thread to unwind its stack even after another thread has spun
    >> deeply into some recursion of its own.
    >
    > Would it not be quite simple to have multiple stacks, save their stack
    > pointers on the heap of the thrd lib, and when context switching make
    > sure that the right stack pointer is used poiting to the right place?

    That is approximately what thread libraries do, as far as I know. There
    is a list of tread structures, each containing the data needed to resume
    that thread, just like the kernel maintains similar data for the proceses.

    Each thread has a private stack region, and a resuption address on the stack.
    The value of the stack pointer register for the thread is in one of three
    places: in the library's tread structure, in the kernel's process structure,
    or in the stack pointer register, when the thread is actually executing.

    >> A problem with user-space threads is if one thread does a blocking read,
    >> there is no way the thread library can switch to another thread without
    >> aborting or completing that read. The threading library needs to replace
    >> all blocking system calls with its own wrappers, which call non-blocking
    >> equivalents, and take suitable control when that equivalent fails to
    >> deliver immediately.
    >
    > Why is this a problem if your a using signals to preempt? I mean why
    > replace blocking calls. Why not just use the signal, interrupt the
    > blocking operation and context switch? Is it problematic to signal in
    > the middle of a blocking call?

    Because the blocking is at kernel level, and the kernel does "know" or care
    that the process is multiple threads. The kernel simply blocks the process,
    including the threading library.

    When a signal arrives, the kernel must decide to deliver the signal, or not.
    If it does, it must decide to abort the system call (returning with a status
    of EINTR) or restart the system call. The latter means the control will go
    back to the kernel after the signal handler finishes, and then back to the
    program when the system call finishes.

    In any case this is not dependent on the process being multithreaded.

    Perhaps the signal handling routine can switch context and run another
    thread while the kernel thinks you are still running a signal handler.

    Now consider the following: a thread issues a system call of the restartable
    variety, and a signal is delivered while this system call is incomplete.
    The threading library receives the signal in its signal handler. The treading
    library does not even know that the currently active thread is a blocked
    in a system call. It has no way to know that it should switch context, and
    not switch back, until that system call has returned.

    Further, assume that the thread library switches context, and the new context
    issues a second restartable system call. Just assume that both calls are
    read calls that will transfer data from some external network connection
    to a disk buffer. (I don't know what calls are actually restartable,
    anybody can correct me here.) Now the kernel must handle two return
    addresses for the process, one for each system call. It must ensure that
    each return is associated with the correct number of bytes tranferred or
    any other status information. Now you want the two threads to be resumed
    with the correct status, no matter what system call finishes first.
    I guess you see there is a can of worms here.

    >> However, if a program uses a new kind of device, executes some ioctl's that
    >> the threading lib authors were not aware of, or that did not even exist at
    >> the time, then the threading lib will not have a wrapper for that.
    >
    > Again, why would it not work as the signal would interrupt this
    > unexpected blocking operation?

    I guess this is really the same question as above.

    > Thanks a lot for your answers. This is exactly what I was looking for!

    Just a final word: I'm not God. I'm not even a kernel programmer. I could
    be mistaken. It's just my two cents.

    -Enrique


  • Next message: Enrique Perez-Terron: "Re: how do threads work?"

    Relevant Pages

    • Re: how do threads work?
      ... >> kernel for a little help, in the form of regular timer signals. ... it is signals. ... The kernel doesn't care if you switch stack causing such ... one CPU bound thrad and one I/O bound thread. ...
      (comp.os.linux.development.system)
    • Re: how do threads work?
      ... A process cannot consider to switch task unless the flow of instructions ... kernel for a little help, in the form of regular timer signals. ... Signals do force an interruption of the flow of instructions. ...
      (comp.os.linux.development.system)
    • Re: Comments on the KSE option
      ... think M:N support in the kernel is a good idea. ... When the userland thread library issues a non-blocking ... When the userland thread scheduler switches threads ... It must also poll for blocked signals. ...
      (freebsd-current)
    • Re: Function: int recv (Socket, Buffer,Length, Flags) is returning with the error number 4
      ... That's a nice speculation by someone with little clue in kernel ... what it means from a kernel coding standpoint to "interrupt a system call". ... set to EINTR after the handler was run, there was no 'kernel state' to ... kernel can deliver signals at any time to a thread which is currently ...
      (comp.unix.programmer)
    • Re: Direct Linux syscalls
      ... > int 80h still exist. ... the "sysenter" has better performance because it's a CPU ... One of the slowest instructions turns out to be "int" (especially when also ... changing from the "user stack" to the "kernel stack" and so on and so forth ...
      (comp.os.linux.development.apps)

  • Quantcast