Info

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)

The function passed with the fn pointer is executed in the generated thread, and the argument specified in arg is automatically passed to the function.16 clone flags can be specified in flags.

The first task of kernel_thread is to construct a pt_regs instance in which the registers are supplied with suitable values, as would be the case with a regular fork system call. Then the familiar do_fork function is invoked.

p = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);

15On multiprocessor systems, the processes genuinely execute in parallel; on single-processor systems, the scheduler simulates parallel execution.

16Arguments allow the function to be used for different purposes by indicating what needs to be done.

Because kernel threads are generated by the kernel itself, two special points should be noted:

1. They execute in the supervisor mode of the CPU, not in the user mode (see Chapter 1).

2. They may access only the kernel part of virtual address space (all addresses above task_size) but not the virtual user area.

Recall from above that the two pointers to mm_structs are contained in the task structure: <sched.h>

struct task_struct {

struct mm_struct *mm, *active_mm;

The total virtual address space of a system is separated into two parts on most machines: The lower portion is accessible by userland programs, and the upper part is reserved for the kernel. When the kernel is running on behalf of a userland program to serve a system call, for instance, the userspace portion of the virtual address space is described by the mm_struct instance pointed to by mm (the exact content of this structure is irrelevant for now, but is discussed in Chapter 4). Every time the kernel performs a context switch, the userland portion of the virtual address space must be replaced to match the then-running process.

This provides some room for optimization, which goes by the name lazy TLB handling: Since kernel threads are not associated with any particular userland process, the kernel does not need to rearrange the userland portion of the virtual address space and can just leave the old setting in place. Since any userland process can have been running before a kernel thread, the contents of the userspace part are essentially random, and the kernel thread must not modify it. To signalize that the userspace portion must not be accessed, mm is set to a NULL pointer. However, since the kernel must know what is currently contained in the userspace, a pointer to the mm_struct describing it is preserved in active_mm.

Why are processes without an mm pointer called lazy TLB processes? Suppose that the process that runs after a kernel thread is the same process that has run before. In this case, the kernel does not need to modify the userspace address tables, and the information in the translation lookaside buffers is still valid. A switch (and a corresponding clearance of TLB data) is only required when a different userland process from before executes after the kernel thread.

Notice that when the kernel is operating in process context, mm and active_mm have identical values.

A kernel thread can be implemented in one of two ways. The older variant — which is still in use in some places in the kernel — is to pass a function directly to kernel_thread. The function is then responsible to assist the kernel in the transformation into a daemon by invoking daemonize. This results in the following actions:

1. The function frees all resources (e.g., memory context, file descriptors, etc.) of the user process as whose child the kernel thread was started because otherwise these would be pinned until the end of the thread — this is not desirable because daemons usually run until the system is shut down. As each daemon operates only in the address area of the kernel, it does not even need these resources.

2. daemonize blocks the receipt of signals.

3. init is used as the parent process of the daemon.

The more modern possibility to create a kernel thread is the auxiliary function kthread_create. kernel/kthread.c struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...)

The function creates a new kernel thread with its name given by namefmt. Initially, the thread will be stopped. To start it, wake_up_process needs to be used. After this, the thread function given in threadfn will be called with data as argument.

As an alternative, the macro kthread_run (which uses the same arguments as kthread_create) will call kthread_create to create the new thread, but will wake it up immediately. A kernel thread can also be bound to a particular CPU by using kthread_create_cpu instead of kthread_create.

Kernel threads appear in the system process list but are enclosed in square brackets in the output of ps to differentiate them from normal processes.

[email protected]> ps

fax

PID TTY

STAT

TIME

COMMAND

2 ?

S<

0

00

[kthreadd]

3 ?

S<

0

00

_ [migration/0]

4 ?

S<

0

00

_ [ksoftirqd/0]

5 ?

S<

0

00

_ [migration/1]

6 ?

S<

0

00

_ [ksoftirqd/1]

52 ?

S<

0

00

_ [kblockd/3]

55 ?

S<

0

00

_ [kacpid]

56 ?

S<

0

00

_ [kacpi_notify]

If a kernel thread is bound to a particular CPU, the CPU's number is noted after the slash.

Continue reading here: Kernelexecc

Was this article helpful?

0 0