Linux Kernel Architecture

(Jacob Rumans) #1

Chapter 2: Process Management and Scheduling


desire to share all filesystem information with the parent (CLONE_FS). It’s not complicated to catch this
combination and return an error code:


kernel/fork.c
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *child_tidptr,
struct pid *pid)
{
int retval;
struct task_struct *p;
int cgroup_callbacks_done = 0;

if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
...

This is also a good place to recall from the introduction that Linux sometimes has to return a pointer if
an operation succeeds, and an error code if something fails. Unfortunately, the C language only allows
a single direct return value per function, so any information about possible errors has to be encoded
into the pointer. While pointers can in general point to arbitrary locations in memory, each architecture
supported by Linux has a region in virtual address space that starts from virtual address 0 and goes at
least 4 KiB far where no senseful information can live. The kernel can thus reuse this pointer range to
encode error codes: If the return value offorkpoints to an address within the aforementioned range,
then the call has failed, and the reason can be determined by the numerical value of the pointer.ERR_PTR
is a helper macro to perform the encoding of the numerical constant-EINVAL(invalid operation) into a
pointer.


Some further flag checks are required:


❑ When a thread is created withCLONE_THREAD, signal sharing must be activated with
CLONE_SIGHAND. Individual threads in a thread group cannot be addressed by a signal.
❑ Shared signal handlers can only be provided if the virtual address space is shared between par-
ent and child (CLONE_VM). Transitive thinking reveals that threads, therefore, also have to share
the address space with the parent.

Once the kernel has established that the flag set does not contradict itself,dup_task_structis used to
create an identical copy of the task structure of the parent process. The newtask_structinstance for the
child can be allocated at any point in kernel memory that happens to be free (see Chapter 3, in which the
allocation mechanisms used for this purpose are described).


The task structures for parent and child differ onlyin one element: A new kernel mode stack is allocated
for the new process. A pointer to it is stored intask_struct->stack. Usually the stack is stored in a
union withthread_info, which holds all required processor-specific low-level information about the
thread.


<sched.h>
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
Free download pdf