Wednesday, January 10, 2007

IPC: Shared Memory Overview on Linux Kernel.

Key points:

- Shared memory mechanism allows processes to share a memory area with other processes.

- Shared memory is an IPC resource and is peristent. This means shared memory will be available to processes till the system is up or the shared memory is explicity removed by some process using appropriate system call.

- Linux Kernel associates each IPC resource (Shared memory here) with an unique 32-bit IPC identifier (shmget() takes an IPC key as input and return this IPC identifier).

- This IPC identifier can be used by processes to communicate with each other through the shared memory (or any other corresponding IPC resource).

- A processes that need to access a shared memory region is required to add a new memory region to its address space (equivalent to mapping the page frames of shared memory). Each process can map the shared memory to a different address (than other processes) in its virtual address space.

***
- Each IPC shared memory region is associated with a file belonging to the "shm" special filesystem. The "shm" filesystem has no mount point in the system directory tree, which effectively means: files on "shm" cannot be accessesed through regular Virtual File System calls. To access a "shm" file, a process needs to attach the shared memory (shmat()) which creates a shared memory mapping (mmap) of the file in the address space of the process.

- Pages belonging to a shared memory region can be swapped out. So to free memory under tight memory situation, swap area can be used to reclaim the page used by shared memory. The point to be taken care of is: since the shared memory region is persistent, kernel cannot discard these pages even when they are no longer used by any process.

Reference: Understanding Linux Kernel, Bovet & Cesati

Sunday, December 31, 2006

Timers:

A System needs to keep account of time for various purpose. For e.g. to carry out rescheduling, waiting for some specified amount of time or for knowing the wall time.

The support for managing time is provided by means of two hardware devices: the system timer (responsible for timer interrupts and updates jiffies) and the real-time clock.

RTC: RTC or Real time clock is generally used for providing the wall time. It is on (non-volatile) even when the system is off, through the CMOS battery prsent on the system board. Kernel reads the RTC at booting time to keep track of wall time.

Sytem Timer: System Timer is required to generate interrupts at periodically. Generally provided through via clocks osciallating at programmable frequency. In x86, it is provided through PIT (programmable interrupt timer). Kernel can program the PIT to define the system timer interrupt (0) frequency Hz.

Saturday, December 30, 2006

Kernel Synchronisation:
Key points:

- Kernel Synchronisation is required to prevent shared data structures and resources from inconsistent/incorrect state.
- This may happen due to multiple threads of execution inside kernel, manipulating and working on shared data structures and resources. For e.g. muliple threads working on request queues if allowed conncurrent access may leave the queue inconsistent.
- In SMP scenario also same code may get executed on different processors simultaneously leaving a shared resource prone to inconsistency.
- In a premptive kernel (2.6), a reschedule may happen at any time (almost any time :).
- For all the above reasons "critical regions of codes" needs protection and to avoid race condition. Code paths that access and manipulate shared data are called critical regions.
- Atomic Operations and locking (implemented through atomic operations) can help in kernel synchronisation. Generally Processors provide instructions to atomically perform operations like i++ (read, manipulate and write).
- Locks (busy wait or sleep) are only advisory not mandatory.

- pseudo-concurrency: interleaving of two operations rather than actually occuring togethe (true concurrency can occur on smp only)
- Causes of concurrency:- Interrupts, Preemption, Sleep (reschedule), SMP- Code needs to be interrupt-safe, SMP-safe, preempt-safe.
- Deadlocks- Occurs when every thread is waiting for some resource(generally locks) that is already held, so no possiblitliy of success. To avoid this, whenever more than one locks needs to be taken, take them in a single order.
NOTE: A code can be SMP safe but still not preempt safe, for e.g. consider code manipulating per-processor data structures. This code will be SMP safe but will need explicit handling
for making it preempt safe.
Some Synchronisation Methods:
- Atomic operations (e.g. test_and_set_bit)
- Barriers (preserve ordering of operations)
- Spin locks
- Semaphores

Saturday, March 25, 2006

Kernel Threads
No post on this blog for a long time, but now will try to keep putting something useful (hopefully) :)

As a start just putting some basic starting material for Linux kernel threads which is incomplete :) and in a horrible shape, but yet...

Getting Started with KERNEL THREADS
1. Introduction
First of all Kernel threads are not to be confused with processes or threads created by processes. Kernel threads are a mechanism used by the Linux kernel to perform some of the important tasks in background. Some examples of kernel threads are: pdflush, kblockd etc.
The significant difference between kernel threads and normal processes is that kernel threads do not have an address space (the mm pointer is NULL). A kernel thead executes in kernel-space only and do not context switch into user space. Kernel threads are, however, schedulable and preemptable as normal process. When a kernel thread is created it is assigned a new task_struct.
2. Difference between a kernel thread and a kernel module:
Many newbie assume that a kernel thread is same as a kernel module. This is wrong perception.
• A kernel module is a piece of code placed in the kernel address space. This is similar to shared libraries. The module can contain data and code as you wish. Being in shared memory the module and it's code can be accessed by any thread in kernel mode. When the module is loaded a special initialization function will be called and should quickly return success or failure. Before the module is unloaded an optional cleanup function is called, this must do whatever cleanup needs to be done.
• A kernel thread is a process without an user address space. When a process is in kernel mode there is little difference, they all share the same kernel address space. If a module has registered functions in the kernel, they can be called by any process and even by multiple processes at the same time.
3. The mm_struct and Kernel Threads
Kernel Threads do not have a process address space and, therefore, do not have an associated memory descriptor. Thus, the mm field of a kernel thread’s process descriptor is NULL. Basically, this is the definition of a kernel thread-they have no user context.
This is fine, because kernel threads do not ever access any user-space memory (whose would they access?). Because kernel threads do not have any pages in user-space, they not really deserve their own memory descriptor and page tables. Despite this, kernel threads need some of the data, such as the page tables, even to access kernel memory. To provide kernel threads the needed data, without wasting memory on a memory descriptor and page tables, or wasting processor cycles to switch to a new address space whenever a kernel thread begins running, kernel threads use the memory descriptor of whatever task ran previously.
Whenever a process is scheduled, the process address space referenced by their mm field is loaded. The active_mm field in the process descriptor is then updated to refer to the new address space. Kernel threads do not have an address space and mm is NULL. Therefore, when a kernel thread is scheduled, the kernel notices that mm is NULL and keeps the previous process’s address space loaded. The kernel then updates the active_mm field of the kernel thread’s process descriptor to refer to the previous process’s memory descriptor. The kernel thread can then use the previous process’s page tables as needed. Because kernel threads do not access user-space memory, they only make use of the information in the address space pertaining to kernel memory, which is the same for all process.
4. Creation of a Kernel Thread
A new kernel thread can be spawned using this interface:

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

The initial function usually implements a loop in which the kernel thread wakes up as needed, performs the required task and sleeps again.

This will create a new kernel thread; the return value from kernel_thread is interpreted in the same way as the return value from sys_fork. That is a positive number is the pid of the created thread, and a negative number indicates an error condition. A return value of zero should be impossible, because the child terminates after calling fn and does not return to the caller of kernel_thread. The action performed by the new thread is equivalent to the statement exit(fn(arg));. The arg parameter is often used to pass a pointer to a struct to the newly created thread, take care to ensure that the struct still exist when the thread needs it. If the struct is a local variable the thread calling kernel_thread could easily have returned before the struct has been read. If arg is not needed it is usually just filled with the value NULL.
5. Example Kernel Thread
#include
#include
#include
#include
int my_thread(void *p){
printk("hello thread\n");
daemonize ("my_thread");
siginitset (¤t->blocked, sigmask(SIGKILL));
while (1){
set_current_state (TASK_INTERRUPTIBLE);
schedule_timeout (5*HZ);
printk ("inside thread: %d\n", current->pid);
};
return 0;
}
int init_module()
{
kernel_thread((int (*)(void *))my_thread,NULL,0);
return 0;
}
void cleanup_module ()
{
kill_proc (current->pid, SIGKILL, 1);
}
MODULE_LICENSE("GPL");



PENDING
Explanation of “daemonize” and (reparent_to_init)
Explanation of “kernel_mode_helper”

References
Linux Kernel Development by Robert Love
http://www.scs.ch/~frey/linux/kernelthreads.html
http://www.brics.dk/~kasperd/comp.os.linux.development.faq.html

Thursday, August 18, 2005

"Unable to open an initial console"

The following message provide the workaround to deal with the problem of booting in custom compliation of kernel specially on Fedora core 3. The system fails to boot giving a "unable to open an initial console".
I have taken this solution as it is from a newsgroup site.

Reason of problem:
-------------------
During the boot process linux expects /dev/console {along with /dev/zero and
/dev/null}entries to be created during the boot process. If it doesn't found
any of the entries, it simply can not open the console device.

In fedora core 3 initrd is responsible for creating the /dev/console related
entries (if you are using initrd for booting up your machine) using udev.
Also fedora core 3 distribution has cutomized udev to work with the
distribution.

Interested readers can further read how udev works and customized for Fedora
core 3.

The solutions:
-------------
The simple solution is to create the entries in /dev for console manually
and don't use initrd for booting the machine (or in other words do not
depend on udev to create console entries during bootup process). Use
following set of commands: -

mkdir /tmp/dev
mount --move /dev /tmp/dev
sbin/MAKEDEV null console zero
mount --move /tmp/dev /dev

Now reboot the machine without initrd. It should work.
This is just a work around and
not the exact solution. Correct solution would be to use latest mkinitrd and
udev packages and use them for FC3.

test........