DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
Historical Background
The user errno value was originally kept in the TCB and could be accesseed accessed via an OS system call called __errno().
# define errno *__errno()
The errno value ws was kept in the thread's TCB in the pterrno field. The __errno()function finds the TCB associated with the current thread and returns a pointer to that errno storage location.
This worked in the FLAT build mode in because it dereferencesdthe dereferenced the address of the errno in TCB as both an RVALUE:
errcode == errno;
And as an LVALUE:
errno = errodeerrcode;
That works, however, it is rather inefficient.
...
void set_errno(int errcode);
int get_errno(void);
Accessing the errno via these functions in PROTECTED or KERNEL mode is a huge performance hit, because the calls must go through a system call which is implemented as a software interrupt.
And in this case, the errno is defined to be:
# define errno get_errno()
This works fine as an RVALUE:
errcode == errno;
But will cause a compilation error if used as an LVALUE:
errno = errodeerrcode;
Instead user code must explicitly call set_errno(errcode). This a a violation of the accepted usage of the POSIX errno variable.
TLS-Based errno
The current solution has moved the errno storage location out of the proctected protected TCB memory and into the unprotected application thread's stack memory. Then it can be access accessed with the appropriate TLS (Thread Local Storage) interfaces.
...
Historically, TLS worked by aligning the stack base address then simplying simply AND'ing the current stack pointer to obtain the base address of the stack where the TLS data can be found (as struct tls_info_s). This mechanism is very efficient: No OS system call is required, application logic can obtain the TLS data directly form its stack via AND'ing and casting.
...
- The alignment must be large. That is because it also determines the maximum size of the thread's stack. If the size of the thread's stack exceeds the maximum value determined by the alignment, then the AND operation will alias to the wrong address.
- If all of the addresses are highly aligned then (1) you need to have much more memory available for stack allocation. This is because (2) the large alignment cause causes bad memory fragmentation and degraded use of memory.
An alternative way to get the stack base address address is to call into the OS to get the unaligned stack base address. This involves a system call, but is more usable that other alernatives alternatives in the FLAT and PROTECTED modes.
...
- Moved the
errnostorage location out of the TCB and in TLS (into thestruct tls_info_s). - Modifed Modified the
errnoaccess definitions and logic that was insched/errnoto use the TLS logic in user space. That logic now resides inlibs/libc/errnosince it is now a user library interface, not a core OS interface. - TLS is now enabled by default. It is enabled in the unaligned mode for the FLAT and PROTECTED build modes but in the hightly highly efficient aligned mode for KERNEL build mode.
- There are no longer an OS system calls related to the error (with the exception of a call to get the
struct tls_info_sin the unaligned TLS mode).
...
TLS is a non-standard, but more general interface. It differs from pthread-specific data only in that its semantics are general; the semantics of the pthread-specific data interfaces are focused on pthreads. But they really should shared share the same common underly underlying logic.
Currently, there are fourTLS four TLS interfaces:
int tls_alloc(void);int tls_free(int tlsindex);uintptr_t tls_get_value(int tlsindex);int tls_set_value(int tlsindex, uintptr_t tlsvalue);
...
These interfaces are basically adaptations from the Windows TLS interfaces which are directly anlagous analogous to the POSIX pthread-specific data interfaces, adapted to NuttX coding standards (see, for example, links available at https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsalloc). There are no POSIX TLS interfaces and Linux (actual GLIBC) does not provide a good mechanism; it relies on a storage class that is specific to ELF binaries. That is not useful in an embedded system where no ELF information is present.
...
We should never access the errno from kernel space. This is especially dangerous from kernel space because we can't be certain which user address environment is in effect or which stack is being used (if we were to use separate kernel mode stacks in the future as Linux does ... for security reasons).
...
- A user callable function, say
osapi()that sets theerrnovalue is necessary (and may implement cancellation points) and - An internal OS version which might then be
nx_osapi()which does not modify theerrnovalue.
Ideally, all of the user callable OS interfaces (the osapi()) should be moved to the location in libs/libc the system call (sycall/) should be redirected to nx_osapi(). A complication to doing this is that the OS interfaces which are also cancellation points call special internal interfaces, enter_cancellation_point() and leave_cancellation_point() that are not available from user space. So some addition partitioning would be need to accomplish this.
...
Another data item that should, eventually, be included in the TLS data is the process ID (pid) if of the currently exectuting executing thread. In some analysis of PROTECTED and KERNEL builds, it was found that getpid() was the most highly accessed OS interface. By moving the pid PID into TLS, we could eliminate this system call overhead (at least in the aligned TLS case). The same mechanism would be used by pthread_self() which, in NuttX, would be the equivalent function, but following pthread semantics.
...
getopt() is an important C library function used by applications for parsing of task command line paramtersparameters. It is, however, not re-entrant due to the use of global variables: optarg, opterr, optind, and optopt. There are several, non-standard implementations for a re-entrant version of called getopt_r(), however, these are all wildly different and do not conform to any standard. Non-standard interfaces are not desirable in NuttX.
...