Kernel threads are special "tasks" that reside within the OS. They are similar to application tasks, so why can't they have pthreads like application tasks?

The FLAT build

In the FLAT build, kernel threads are, in fact, identical to application threads except that (1) they follow some slightly different syntax and (2) can only use OS internal interfaces, never application interfaces. Since they are otherwise identical, there is really no technical reason why pthreads could not be supported.

The real reasons in this case is (1) it is inappropriate, and (2) it is incompatible with the PROTECTED and KERNEL builds where pthreads cannot be supported.

Why inappropriate?  Because..

pthread Interfaces Are User Interfaces

In all Unix systems, pthread interface support is not provided by the operating system but rather by the user-facing C library. The role of the OS is only to provide some low level hooks needed by C library implementation of pthread interfaces.  But all of the implementation is in the C library and only for use by applications.

That is not the current situation in NuttX; currently pthread support is deeply entangled in the OS. This is problem that must be fixed someday and must not exploited as some permanent feature of the OS. It is not. It will go away some day when NuttX becomes a mature Unix-family system.

As user facing interfaces, pthread interfaces include some behaviors that are not appropriate within the OS. Inappropriate behaviors include modification of the errno value and cancellation points. Those are user-only features. While pthread interfaces do not, in general, modify the errno setting they do create cancellation points which is not desirable within the OS.

PROTECTED and KERNEL Builds

The PROTECTED and KERNEL builds differ from the FLAT build in that they segregate the memory into two regions: A privileged kernel address space and unprivileged, user address space(s). The primary difference is that the PROTECTED build uses a physical address space and different regions of the physical address space have different properties. This is usually accomplished using a Memory Protection Unit (MPU). There is one protected kernel address space and one unprotected user address space.

The KERNEL build, on the other hand, uses a Memory Management Unit (MMU) to create a virtual address space in which there is still a separate protected kernel address space, but many user address spaces for user programs. These are usually called processes. This is the familiar build model that you find with high-end Unix-like systems such as Linux.

See Memory Configurations for additional information

Address Spaces, Memory Allocators, and User Mode

What would happen if you tried to create a pthread in the PROTECTED or KERNEL build? The effect of these address space differences become very pronounced. pthreads are, by their very definition, user-space threads. That means that the OS will attempt to create a user-space environment for the pthread: The thread's stack and other resources.

And when the pthread is started, it will be started in user mode, not kernel mode.

It would require a significant change to the OS to alter that behavior and that is not under consideration (because the change is also inappropriate).

Entry points

Because the application space and the kernel space are separately built and separately linked in the PROTECTED and KERNEL builds. No application addresses are known by the kernel and no kernel addresses are known by the application (applications interface via system call traps, not C function calls). So what address would the kernel thread provide for the pthread entry point? A known kernel space address? Of course, the system would crash with a memory fault immediately if a protected address were executed in user mode.

Mutexes

Okay, so there are no kernel pthreads. But could other pthread resources be usable in the kernel. The short answer is no. Consider mutexes, for example. By definition, a mutex can only be used within threads of the current process (or task groups as they are often called in NuttX). They have no meaning outside of the task group. Since the kernel thread "task group" can have no pthreads, how could these mutexes be used under the proper standard definition of what a mutex must be?

This is true of all pthread interfaces:  None of the pthread interfaces were intended from inter-process communications; only for inter-pthread communications within the same process (task group).

Roadmap

As alluded to before about the appropriateness of pthread in the OS. There is a roadmap for these kinds of features. That roadmap is to continue to conform strictly to the standard OS definitions of Opengroup.org and to continue to evolve as a fully compliant, very standard, tiny Unix-like operating system.

  • Part of this is getting all user interfaces out of the OS.
  • Another part is migrating all pthread support out of operating system and into the user-facing C library.
  • An additional objective on the roadmap is to streamline the kernel threads and to disconnect them from task groups.  Kernel threads should not be part of any task group.

In regard to this last point, the following is taken from Apache NuttX Issue 1108: "Remove streams from Kernel Threads":

No Streams in Kernel Threads

Kernel threads are not permitted to use the C library buffer I/O, "stream", interfaces. Those are interfaces like fopen, fread, fwrite, fclose, etc. Those are strictly for use by user applications. This is because these functions modify errno variables and create cancellation points and perhaps other things that are undesirable within the OS.

The thread create logic is largely the same for both kernel threads and for application threads: They both allocate a large buffer from the user memory pool for the stream FILE array. Since kernel threads this is a waste of memory since the kernel threads should not be using streams.

Remove Kernel Thread Stream Allocation

The proposal is (1) Verify that there is no use of streams within the OS, (2) remove the stream allocation for kernel threads, and (3) assure that there are proper checks in place so that there are no uses of the NULL stream array pointer.

For the most part, the OS is clean, there are essentially no use of streams within the OS. There are, however, a few violations of this that will need to be fixed; fopen() is called in some of the lc3850.

Remove the File Descriptor Array Too

A second phase would be to remove the file descriptor array from kernel threads. File descriptors are, again, only for use by applications; within the OS, file access is done using the struct file (aka, FILE), structure directly. However, I suspect that there are many hidden uses of file descriptors in the system. For one, file_open() which opens the detached file, is not fully implemented; it cheats and uses file descriptors. So let's consider removal of the file descriptor allocation as a second step after the stream allocations have been removed.

  • No labels