??xml version="1.0" encoding="utf-8" standalone="yes"?> The kernel has several differences compared to normal
user-space applications that, although not making it necessarily harder to
program than user-space, certainly provide unique challenges to kernel
development.
These differences make the kernel a beast of a different
nature. Some of the usual rules are bent; other rules are entirely new. Although
some of the differences are obvious (we all know the kernel can do anything it
wants), others are not so obvious. The most important of these differences
are The kernel does not have access to the C library. The kernel is coded in GNU C. The kernel lacks memory protection like user-space. The kernel cannot easily use floating point. The kernel has a small fixed-size stack. Because the kernel has asynchronous interrupts, is preemptive,
and supports SMP, synchronization and concurrency are major concerns within the
kernel. Portability is important. Let's briefly look at each of these issues because all kernel
development must keep them in mind. Unlike a user-space application, the kernel is not linked
against the standard C library (or any other library, for that matter). There
are multiple reasons for this, including some chicken-and-the-egg situations,
but the primary reason is speed and size. The full C libraryor even a decent
subset of itis too large and too inefficient for the kernel. Do not fret: Many of the usual libc functions have been
implemented inside the kernel. For example, the common string manipulation
functions are in
lib/string.c. Just include
<linux/string.h> and have at them.
When I talk about header files hereor elsewhere in this bookI
am referring to the kernel header files that are part of the kernel source tree.
Kernel source files cannot include outside headers, just as they cannot use
outside libraries.
sudo make-kpkg --initrd --append-to-version=dell1400 kernel_image kernel-headers
sudo dpkg -i linux-image-*.deb
references:
1.http://forum.ubuntu.org.cn/viewtopic.php?t=134404
]]>
Purpose
This examples shows how to create and stop a kernel thread.
The driver is implemented as a loadable module. In the init_module() routine five kernel threads are created. This kernel threads sleep one second, wake up, print a message and fall asleep again. On unload of the module (cleanup_module), the kernel threads are killed.
The example has been tested with Linux kernel 2.4.2 on Intel (uni processor only) and Alpha platform (COMPAQ Personal Workstation 500au (uni processor), DS20 and ES40 (SMP).
A version for the 2.2 kernel can be found here. Note: depending on the context of the creator of the threads the new threads may inherit properties from the parent you do not want to have. The new version avoids this by having keventd create the threads. The 2.2. kernel do not have a keventd, so this approach is not implementable there.
Functions in example
start_kthread: creates a new kernel thread. Can be called from any process context but not from interrupt. The functions blocks until the thread started.
stop_kthread: stop the thread. Can be called from any process context but the thread to be terminated. Cannot be called from interrupt context. The function blocks until the thread terminated.
init_kthread: sets the environment of the new threads. Is to be called out of the created thread.
exit_kthread: needs to be called by the thread to be terminated on exit
Creation of new Thread
A new thread is created with kernel_thread(). The thread inherits properties from its parents. To make sure that we do not get any weired properties, we let keventd create the new thread.
The new thread is created with start_kthread(). It uses a semaphore to block until the new thread is running. A down() blocks the start_kthread() routine until the corresponding up() call in init_kthread() is executed.
The new thread must call init_kthread() in order to let the creator continue.
Stop of new Thread
stop_kthread() sets a flag that the thread uses to determine whether do die or not and sends a SIGKILL to the thread. This signal causes the thread to be woken up. On wakeup it will check for the flag and then terminate itself by calling exit_kthread and returning from the thread function. With a semaphore the stop_kthread() function blocks until the thread terminated.
Initialization of new Thread
Within the new created thread, init_kthread() needs to be called. This function sets a signal mask, initialises a wait queue, the termination flag and sets a new name for the thread. With a up() call it notifies the creator that the setup is done.
Exit of new Thread
When the thread receives the notification to terminate itself, is calls the exit_kthread() function. It notifies the stop_kthread() function that it terminated with an up() call.
The new Thread itself
The new thread is implemented in the example_thread() function. It runs an endless loop (for(;;)). In the loop it falls asleep with the interruptible_sleep_on_timeout() function. It comes out of this function either when the timeout expires or when a signal got caught.
The "work" in the thread is to print out a message with printk.
Kernel Versions
The example has been tested on 2.4.2.
Example Device Driver Code
The example consists of four files: kthread.h, kthread.c, thread_drv.c and a Makefile
kthread.h
#ifndef _KTHREAD_H
#define _KTHREAD_H
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#include <asm/unistd.h>
#include <asm/semaphore.h>
/* a structure to store all information we need
for our thread */
typedef struct kthread_struct
{
/* private data */
/* Linux task structure of thread */
struct task_struct *thread;
/* Task queue need to launch thread */
struct tq_struct tq;
/* function to be started as thread */
void (*function) (struct kthread_struct *kthread);
/* semaphore needed on start and creation of thread. */
struct semaphore startstop_sem;
/* public data */
/* queue thread is waiting on. Gets initialized by
init_kthread, can be used by thread itself.
*/
wait_queue_head_t queue;
/* flag to tell thread whether to die or not.
When the thread receives a signal, it must check
the value of terminate and call exit_kthread and terminate
if set.
*/
int terminate;
/* additional data to pass to kernel thread */
void *arg;
} kthread_t;
/* prototypes */
/* start new kthread (called by creator) */
void start_kthread(void (*func)(kthread_t *), kthread_t *kthread);
/* stop a running thread (called by "killer") */
void stop_kthread(kthread_t *kthread);
/* setup thread environment (called by new thread) */
void init_kthread(kthread_t *kthread, char *name);
/* cleanup thread environment (called by thread upon receiving termination signal) */
void exit_kthread(kthread_t *kthread);
#endif
kthread.c
#include <linux/config.h>
#include <linux/version.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <asm/semaphore.h>
#include <asm/smplock.h>
#include "kthread.h"
/* private functions */
static void kthread_launcher(void *data)
{
kthread_t *kthread = data;
kernel_thread((int (*)(void *))kthread->function, (void *)kthread, 0);
}
/* public functions */
/* create a new kernel thread. Called by the creator. */
void start_kthread(void (*func)(kthread_t *), kthread_t *kthread)
{
/* initialize the semaphore:
we start with the semaphore locked. The new kernel
thread will setup its stuff and unlock it. This
control flow (the one that creates the thread) blocks
in the down operation below until the thread has reached
the up() operation.
*/
init_MUTEX_LOCKED(&kthread->startstop_sem);
/* store the function to be executed in the data passed to
the launcher */
kthread->function=func;
/* create the new thread my running a task through keventd */
/* initialize the task queue structure */
kthread->tq.sync = 0;
INIT_LIST_HEAD(&kthread->tq.list);
kthread->tq.routine = kthread_launcher;
kthread->tq.data = kthread;
/* and schedule it for execution */
schedule_task(&kthread->tq);
/* wait till it has reached the setup_thread routine */
down(&kthread->startstop_sem);
}
/* stop a kernel thread. Called by the removing instance */
void stop_kthread(kthread_t *kthread)
{
if (kthread->thread == NULL)
{
printk("stop_kthread: killing non existing thread!\n");
return;
}
/* this function needs to be protected with the big
kernel lock (lock_kernel()). The lock must be
grabbed before changing the terminate
flag and released after the down() call. */
lock_kernel();
/* initialize the semaphore. We lock it here, the
leave_thread call of the thread to be terminated
will unlock it. As soon as we see the semaphore
unlocked, we know that the thread has exited.
*/
init_MUTEX_LOCKED(&kthread->startstop_sem);
/* We need to do a memory barrier here to be sure that
the flags are visible on all CPUs.
*/
mb();
/* set flag to request thread termination */
kthread->terminate = 1;
/* We need to do a memory barrier here to be sure that
the flags are visible on all CPUs.
*/
mb();
kill_proc(kthread->thread->pid, SIGKILL, 1);
/* block till thread terminated */
down(&kthread->startstop_sem);
/* release the big kernel lock */
unlock_kernel();
/* now we are sure the thread is in zombie state. We
notify keventd to clean the process up.
*/
kill_proc(2, SIGCHLD, 1);
}
/* initialize new created thread. Called by the new thread. */
void init_kthread(kthread_t *kthread, char *name)
{
/* lock the kernel. A new kernel thread starts without
the big kernel lock, regardless of the lock state
of the creator (the lock level is *not* inheritated)
*/
lock_kernel();
/* fill in thread structure */
kthread->thread = current;
/* set signal mask to what we want to respond */
siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
/* initialise wait queue */
init_waitqueue_head(&kthread->queue);
/* initialise termination flag */
kthread->terminate = 0;
/* set name of this process (max 15 chars + 0 !) */
sprintf(current->comm, name);
/* let others run */
unlock_kernel();
/* tell the creator that we are ready and let him continue */
up(&kthread->startstop_sem);
}
/* cleanup of thread. Called by the exiting thread. */
void exit_kthread(kthread_t *kthread)
{
/* we are terminating */
/* lock the kernel, the exit will unlock it */
lock_kernel();
kthread->thread = NULL;
mb();
/* notify the stop_kthread() routine that we are terminating. */
up(&kthread->startstop_sem);
/* the kernel_thread that called clone() does a do_exit here. */
/* there is no race here between execution of the "killer" and real termination
of the thread (race window between up and do_exit), since both the
thread and the "killer" function are running with the kernel lock held.
The kernel lock will be freed after the thread exited, so the code
is really not executed anymore as soon as the unload functions gets
the kernel lock back.
The init process may not have made the cleanup of the process here,
but the cleanup can be done safely with the module unloaded.
*/
}
thread_drv.c
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include "kthread.h"
#define NTHREADS 5
/* the variable that contains the thread data */
kthread_t example[NTHREADS];
/* prototype for the example thread */
static void example_thread(kthread_t *kthread);
/* load the module */
int init_module(void)
{
int i;
/* create new kernel threads */
for (i=0; i <NTHREADS; i++)
start_kthread(example_thread, &example);
return(0);
}
/* remove the module */
void cleanup_module(void)
{
int i;
/* terminate the kernel threads */
for (i=0; i<NTHREADS; i++)
stop_kthread(&example);
return;
}
/* this is the thread function that we are executing */
static void example_thread(kthread_t *kthread)
{
/* setup the thread environment */
init_kthread(kthread, "example thread");
printk("hi, here is the kernel thread\n");
/* an endless loop in which we are doing our work */
for(;;)
{
/* fall asleep for one second */
interruptible_sleep_on_timeout(&kthread->queue, HZ);
/* We need to do a memory barrier here to be sure that
the flags are visible on all CPUs.
*/
mb();
/* here we are back from sleep, either due to the timeout
(one second), or because we caught a signal.
*/
if (kthread->terminate)
{
/* we received a request to terminate ourself */
break;
}
/* this is normal work to do */
printk("example thread: thread woke up\n");
}
/* here we go only in case of termination of the thread */
/* cleanup the thread, leave */
exit_kthread(kthread);
/* returning from the thread here calls the exit functions */
}
Makefile
# set to your kernel tree
KERNEL = /usr/src/linux
# get the Linux architecture. Needed to find proper include file for CFLAGS
ARCH=$(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
# set default flags to compile module
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNEL)/include
CFLAGS+= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing
all: thread_mod.o
# get configuration of kernel
include $(KERNEL)/.config
# modify CFLAGS with architecture specific flags
include $(KERNEL)/arch/${ARCH}/Makefile
# enable the module versions, if configured in kernel source tree
ifdef CONFIG_MODVERSIONS
CFLAGS+= -DMODVERSIONS -include $(KERNEL)/include/linux/modversions.h
endif
# enable SMP, if configured in kernel source tree
ifdef CONFIG_SMP
CFLAGS+= -D__SMP__
endif
# note: we are compiling the driver object file and then linking
# we link it into the module. With just one object file as in
# this example this is not needed. We can just load the object
# file produced by gcc
# link the thread driver module
thread_mod.o: thread_drv.o kthread.o
ld -r -o thread_mod.o thread_drv.o kthread.o
# compile the kthread object file
kthread.o: kthread.c kthread.h
gcc $(CFLAGS) -c kthread.c
# compile the thread driver
thread_drv.o: thread_drv.c kthread.h
gcc $(CFLAGS) -c thread_drv.c
clean:
rm -f *.o
Bugs
The code assumes that keventd is running with PID 2.
Comments, Corrections
Please send comments, corrections etc. to the address below.
from:
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=282973&page=15&view=collapsed&sb=5&o=all
]]>
]]>A Beast of a Different Nature
No libc
Header Files
Of the missing functions, the most familiar is printf(). The kernel does not have access to printf(), but it does have access to printk(). The printk() function copies the formatted string into the kernel log buffer, which is normally read by the syslog program. Usage is similar to printf():
printk("Hello world! A string: %s and an integer: %d\n", a_string, an_integer);
One notable difference between printf() and printk() is that printk() allows you to specify a priority flag. This flag is used by syslogd(8) to decide where to display kernel messages. Here is an example of these priorities:
printk(KERN_ERR "this is an error!\n");
We will use printk() tHRoughout this book. Later chapters have more information on printk().
Like any self-respecting Unix kernel, the Linux kernel is programmed in C. Perhaps surprisingly, the kernel is not programmed in strict ANSI C. Instead, where applicable, the kernel developers make use of various language extensions available in gcc (the GNU Compiler Collection, which contains the C compiler used to compile the kernel and most everything else written in C on a Linux system).
The kernel developers use both ISO C99[1] and GNU C extensions to the C language. These changes wed the Linux kernel to gcc, although recently other compilers, such as the Intel C compiler, have sufficiently supported enough gcc features that they too can compile the Linux kernel. The ISO C99 extensions that the kernel uses are nothing special and, because C99 is an official revision of the C language, are slowly cropping up in a lot of other code. The more interesting, and perhaps unfamiliar, deviations from standard ANSI C are those provided by GNU C. Let's look at some of the more interesting extensions that may show up in kernel code.
[1] ISO C99 is the latest major revision to the ISO C standard. C99 adds numerous enhancements to the previous major revision, ISO C90, including named structure initializers and a complex type. The latter of which you cannot use safely from within the kernel.
GNU C supports inline functions. An inline function is, as its name suggests, inserted inline into each function call site. This eliminates the overhead of function invocation and return (register saving and restore), and allows for potentially more optimization because the compiler can optimize the caller and the called function together. As a downside (nothing in life is free), code size increases because the contents of the function are copied to all the callers, which increases memory consumption and instruction cache footprint. Kernel developers use inline functions for small time-critical functions. Making large functions inline, especially those that are used more than once or are not time critical, is frowned upon by the kernel developers.
An inline function is declared when the keywords static and inline are used as part of the function definition. For example:
static inline void dog(unsigned long tail_size)
The function declaration must precede any usage, or else the compiler cannot make the function inline. Common practice is to place inline functions in header files. Because they are marked static, an exported function is not created. If an inline function is used by only one file, it can instead be placed toward the top of just that file.
In the kernel, using inline functions is preferred over complicated macros for reasons of type safety.
The gcc C compiler enables the embedding of assembly instructions in otherwise normal C functions. This feature, of course, is used in only those parts of the kernel that are unique to a given system architecture.
The asm() compiler directive is used to inline assembly code.
The Linux kernel is programmed in a mixture of C and assembly, with assembly relegated to low-level architecture and fast path code. The vast majority of kernel code is programmed in straight C.
The gcc C compiler has a built-in directive that optimizes conditional branches as either very likely taken or very unlikely taken. The compiler uses the directive to appropriately optimize the branch. The kernel wraps the directive in very easy-to-use macros, likely() and unlikely().
For example, consider an if statement such as the following:
if (foo) {
/* ... */
}
To mark this branch as very unlikely taken (that is, likely not taken):
/* we predict foo is nearly always zero ... */
if (unlikely(foo)) {
/* ... */
}
Conversely, to mark a branch as very likely taken:
/* we predict foo is nearly always nonzero ... */
if (likely(foo)) {
/* ... */
}
You should only use these directives when the branch direction is overwhelmingly a known priori or when you want to optimize a specific case at the cost of the other case. This is an important point: These directives result in a performance boost when the branch is correctly predicted, but a performance loss when the branch is mispredicted. A very common usage for unlikely() and likely() is error conditions. As one might expect, unlikely() finds much more use in the kernel because if statements tend to indicate a special case.
When a user-space application attempts an illegal memory access, the kernel can trap the error, send SIGSEGV, and kill the process. If the kernel attempts an illegal memory access, however, the results are less controlled. (After all, who is going to look after the kernel?) Memory violations in the kernel result in an oops, which is a major kernel error. It should go without saying that you must not illegally access memory, such as dereferencing a NULL pointerbut within the kernel, the stakes are much higher!
Additionally, kernel memory is not pageable. Therefore, every byte of memory you consume is one less byte of available physical memory. Keep that in mind next time you have to add one more feature to the kernel!
When a user-space process uses floating-point instructions, the kernel manages the transition from integer to floating point mode. What the kernel has to do when using floating-point instructions varies by architecture, but the kernel normally catches a trap and does something in response.
Unlike user-space, the kernel does not have the luxury of seamless support for floating point because it cannot trap itself. Using floating point inside the kernel requires manually saving and restoring the floating point registers, among possible other chores. The short answer is: Don't do it; no floating point in the kernel.
User-space can get away with statically allocating tons of variables on the stack, including huge structures and many-element arrays. This behavior is legal because user-space has a large stack that can grow in size dynamically (developers of older, less intelligent operating systemssay, DOSmight recall a time when even user-space had a fixed-sized stack).
The kernel stack is neither large nor dynamic; it is small and fixed in size. The exact size of the kernel's stack varies by architecture. On x86, the stack size is configurable at compile-time and can be either 4 or 8KB. Historically, the kernel stack is two pages, which generally implies that it is 8KB on 32-bit architectures and 16KB on 64-bit architecturesthis size is fixed and absolute. Each process receives its own stack.
The kernel stack is discussed in much greater detail in later chapters.
The kernel is susceptible to race conditions. Unlike a single-threaded user-space application, a number of properties of the kernel allow for concurrent access of shared resources and thus require synchronization to prevent races. Specifically,
Linux is a preemptive multi-tasking operating system. Processes are scheduled and rescheduled at the whim of the kernel's process scheduler. The kernel must synchronize between these tasks.
The Linux kernel supports multiprocessing. Therefore, without proper protection, kernel code executing on two or more processors can access the same resource.
Interrupts occur asynchronously with respect to the currently executing code. Therefore, without proper protection, an interrupt can occur in the midst of accessing a shared resource and the interrupt handler can then access the same resource.
The Linux kernel is preemptive. Therefore, without protection, kernel code can be preempted in favor of different code that then accesses the same resource.
Typical solutions to race conditions include spinlocks and semaphores.
Later chapters provide a thorough discussion of synchronization and concurrency.
Although user-space applications do not have to aim for portability, Linux is a portable operating system and should remain one. This means that architecture-independent C code must correctly compile and run on a wide range of systems, and that architecture-dependent code must be properly segregated in system-specific directories in the kernel source tree.
A handful of rulessuch as remain endian neutral, be 64-bit clean, do not assume the word or page size, and so ongo a long way. Portability is discussed in extreme depth in a later chapter.
If you want to compile the sum-module (source mirrored below), follow these steps:
Create the Makefile in your directory with the sum-module.cobj-m := sum-module.o
KDIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)
default:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Now do amake
... and the sum-module.ko is built.If you get something like this
# makemake: Nothing to be done for `default'.
you need to install the kernel source and compile the kernel first (run "make" at least to the point untilAnother reason for the above error can be, that your browser converted the TAB before $(MAKE) to spaces.
Make sure there is a TAB before $(MAKE).
Install it with install.sh:
#!/bin/shinstall -m 644 sum-module.ko /lib/modules/`uname -r`/kernel/drivers/sum-module.ko
/sbin/depmod -a (adjust the /lib/modules path according to your needs)Now make a
# modprobe sum-module
Or if you don't want to install the module, do this:
# insmod ./sum-module.ko
..and if your system doesn't freeze you've done it right ;-)
For kernel 2.4, the Makefile would look like this:
TARGET := modulename
INCLUDE := -I/lib/modules/`uname -r`/build/includeCFLAGS := -O2 -Wall -DMODULE -D__KERNEL__ -DLINUX
CC := gcc ${TARGET}.o: ${TARGET}.c
$(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c(not yet tested)
sum-module source from: http://www.win.tue.nl/~aeb/linux/lk/lk-9.html/*
* sum-module.c
# modprobe sum-module.o
# ls -l /proc/arith
total 0
dr-xr-xr-x 2 root root 0 Sep 30 12:40 .
dr-xr-xr-x 89 root root 0 Sep 30 12:39 ..
-r--r--r-- 1 root root 0 Sep 30 12:40 sum
# cat /proc/arith/sum
0
# echo 7 > /proc/arith/sum
# echo 5 > /proc/arith/sum
# echo 13 > /proc/arith/sum
# cat /proc/arith/sum
25
# rmmod sum-module
# ls -l /proc/arith
ls: /proc/arith: No such file or directory
#
*/
#include <linux/module.h>
#include <linux/init.h>#include <linux/proc_fs.h>
#include <asm/uaccess.h>
static unsigned long long sum;
static int show_sum(char *buffer, char **start, off_t offset, int length) {
int size;
size = sprintf(buffer, "%lld\n", sum);*start = buffer + offset;
size -= offset;return (size > length) ? length : (size > 0) ? size : 0;
}
/* Expect decimal number of at most 9 digits followed by '\n' */static int add_to_sum(struct file *file, const char *buffer,
unsigned long count, void *data)
{
unsigned long val = 0;char buf[10];
char *endp;if (count > sizeof(buf))
return -EINVAL;if (copy_from_user(buf, buffer, count))
return -EFAULT;
val = simple_strtoul(buf, &endp, 10);
if (*endp != '\n')
return -EINVAL;
return count;
}
static int __init sum_init(void) {
struct proc_dir_entry *proc_arith;
struct proc_dir_entry *proc_arith_sum;
proc_arith = proc_mkdir("arith", 0);if (!proc_arith) {
printk (KERN_ERR "cannot create /proc/arith\n");
return -ENOMEM;
}
proc_arith_sum = create_proc_info_entry("arith/sum", 0, 0, show_sum);
if (!proc_arith_sum) {
printk (KERN_ERR "cannot create /proc/arith/sum\n");
remove_proc_entry("arith", 0);
return -ENOMEM;
}
proc_arith_sum->write_proc = add_to_sum;
return 0;
}
static void __exit sum_exit(void) {
remove_proc_entry("arith/sum", 0);
remove_proc_entry("arith", 0);
}
module_init(sum_init);
module_exit(sum_exit);
MODULE_LICENSE("GPL");
fromQ?/font>
http://www.captain.at/programming/kernel-2.6/
http://blog.ednchina.com/fafen/267973/message.aspx#define MY_FILE "/root/LogFile"
char buf[128];
struct file *file = NULL;
static int __init init(void)
{
mm_segment_t old_fs;
printk("Hello, I'm the module that intends to write messages to file.\n");
if(file == NULL)
file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);
if (IS_ERR(file)) {
printk("error occured while opening file %s, exiting...\n", MY_FILE);
return 0;
}
sprintf(buf,"%s", "The Messages.");
old_fs = get_fs();
set_fs(KERNEL_DS);
file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos);
set_fs(old_fs);
return 0;
}
static void __exit fini(void)
{
if(file != NULL)
filp_close(file, NULL);
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
A kernel thread, sometimes called a LWP (Lightweight Process) is created and scheduled by the kernel. Kernel threads are often more expensive to create than user threads and the system calls to directly create kernel threads are very platform specific.
A user thread is normally created by a threading library and scheduling is managed by the threading library itself (Which runs in user mode). All user threads belong to process that created them. The advantage of user threads is that they are portable.
The major difference can be seen when using multiprocessor systems, user threads completely managed by the threading library can't be ran in parallel on the different CPUs, although this means they will run fine on uniprocessor systems. Since kernel threads use the kernel scheduler, different kernel threads can run on different CPUs.
Many systems implement threading differently,
A many-to-one threading model maps many user processes directly to one kernel thread, the kernel thread can be thought of as the main process.
A one-to-one threading model maps each user thread directly to one kernel thread, this model allows parallel processing on the multiprocessor systems. Each kernel thread can be thought of as a VP (Virtual Process) which is managed by the scheduler.
字符讑֤q是块设备的定义属于操作pȝ的设备访问层Q与实际物理讑֤的特性无必然联系?/p>
讑֤讉K层下面是驱动E序Q所以只要驱动程序提供的方式Q都可以。也是说驱动程序支持stream方式Q那么就可以用这U方式访问,驱动E序如果q支持block方式Q那么你想用哪种方式讉K都可以,典型的比如硬盘式的裸讑֤Q两U都支持块设备(block deviceQ:是一U具有一定结构的随机存取讑֤Q对q种讑֤的读写是按块q行的,他用缓冲区来存放暂时的数据Q待条g成熟后,从缓存一ơ性写入设备或从设备中一ơ性读出放入到~冲区,如磁盘和文gpȝ{?/p>
字符讑֤QCharacter deviceQ:q是一个顺序的数据设备,对这U设备的d是按字符q行的,而且q些字符是连l地形成一个数据流。他不具备缓冲区Q所以对q种讑֤的读写是实时的,如终端、磁带机{?br>pȝ中能够随机(不需要按序Q访问固定大数据片QchunksQ的讑֤被称作块讑֤Q这些数据片q作块。最常见的块讑֤是硬盘,除此以外Q还有Y盘驱动器、CD-ROM驱动器和闪存{等许多其他块设备。注意,它们都是以安装文件系l的方式使用的——这也是块设备一般的讉K方式?/p>
另一U基本的讑֤cd是字W设备。字W设备按照字W流的方式被有序讉KQ像串口和键盘就都属于字W设备。如果一个硬件设备是以字W流的方式被讉K的话Q那应该将它归于字W设备;反过来,如果一个设备是随机Q无序的Q访问的Q那么它属于块讑֤?/p>
q两U类型的讑֤的根本区别在于它们是否可以被随机讉K——换句话说就是,能否在访问设备时随意C一个位|蟩转到另一个位|。D个例子,键盘q种讑֤提供的就是一个数据流Q当你敲?#8220;fox”q个字符串时Q键盘驱动程序会按照和输入完全相同的序q回q个׃个字W组成的数据。如果让键盘驱动E序打ؕ序来读字符Ԍ或读取其他字W,都是没有意义的。所以键盘就是一U典型的字符讑֤Q它提供的就是用户从键盘输入的字W流。对键盘q行L作会得到一个字W流Q首先是“f”Q然后是“o”Q最后是“x”Q最l是文g的结?EOF)。当没h敲键盘时Q字W流是I的。硬盘设备的情况׃大一样了。硬盘设备的驱动可能要求d盘上Q意块的内容,然后又{去读取别的块的内容,而被d的块在磁盘上位置不一定要q箋Q所以说盘可以被随问,而不是以的方式被访问,昄它是一个块讑֤?/p>
内核理块设备要比管理字W设备细致得多,需要考虑的问题和完成的工作相比字W设备来说要复杂许多。这是因为字W设备仅仅需要控制一个位|—当前位|—而块讑֤讉K的位|必能够在介质的不同区间前后移动。所以事实上内核不必提供一个专门的子系l来理字符讑֤Q但是对块设备的理却必要有一个专门的提供服务的子pȝ。不仅仅是因为块讑֤的复杂性远q高于字W设备,更重要的原因是块讑֤Ҏ行性能的要求很高;对硬盘每多一分利用都会对整个pȝ的性能带来提升Q其效果要远q比键盘吞吐速度成倍的提高大得多。另外,我们会看到Q块讑֤的复杂性会U优化留下很大的施展I间.
from:
http://os.51cto.com/art/200909/151133.htm
FILE *fp = fopen(FILENAME,"wb"); while((len = recv(sockfd, buff, sizeof(buff), 0)) > 0) |
fd = open(FILENAME, O_RDONLY); while((len =read(fd, buff, sizeof(buff))) >0) { send(sockfd, buff, len ,0); } close(fd); |
׃我磁盘分区时指定的块大小?096,Z最优读取磁盘数?buff大小设ؓ4096字节.但在试中发现设?024?192不会对传输速度带来影响.
文g大小:9M; 耗时:0.71 - 0.76U?
文g大小:32M; 耗时:2.64 - 2.68U?
文g大小:64M; 耗时:5.36 - 5.43U?
B. 使用sendfile()传输代码D?
off_t offset = 0; fd = open(FILENAME, O_RDONLY); |
文g大小:9M; 耗时:0.71 - 1.08U?
文g大小:32M; 耗时:2.66 - 2.74U?
文g大小:64M; 耗时:5.43 - 6.64U?
gq略有下?Ҏsendfile的man手册,我在使用该函数前调用?br>
int no = 1; |
文g大小:9M; 耗时:0.72 - 0.75U?
文g大小:32M; 耗时:2.66 - 2.68U?
文g大小:64M; 耗时:5.38 - 5.60U?
q样g辑ֈ了传l方式的速度?!不管哪种环境?我用ethereal抓包昄每一个tcp包的playload部分最大也通常?448字节.
看来我的试没有体现?应用层数据的两次拯带来很大的消?q一说法.如果按照存在是有理的说法的?那我想sendfile()在两U情况下才体C?但我却没有环境测?
1. 大ƈ发量的文件服务器或HTTP服务?
2. 内存资源紧张的嵌入式pȝ;
另外,|络上大量的关于tcp选项中的TCP_CORK描述已经q时.在man手册中早已提到该参数可以与TCP_NODELAYl合使用?只是,只要讄了TCP_NODELAY选项?不管是否讄TCP_CORK,包都会立卛_?
----------------------------------------------------------------------
补充:
TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle?#8221;QNagle化在q里的含义是采用Nagle法把较的包组装ؓ更大的?John Nagle是Nagle法的发明hQ后者就是用他的名字来命名的Q他?984q首ơ用q种Ҏ来尝试解决福Ҏ车公司的|络拥塞问题Q欲了解详情请参看IETF RFC 896Q。他解决的问题就是所谓的silly window syndrome Q中文称“愚蠢H口症候群”Q具体含义是Q因为普遍终端应用程序每产生一ơ击键操作就会发送一个包Q而典型情况下一个包会拥有一个字节的数据载荷以及40个字节长的包_于是产生4000%的过载,很轻易地p令网l发生拥??Nagle化后来成了一U标准ƈ且立卛_因特|上得以实现。它现在已经成ؓ~省配置了,但在我们看来Q有些场合下把这一选项x也是合乎需要的?
现在让我们假设某个应用程序发Z一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待生更多的数据然后再一ơ发送两U策略。如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当我们正在发送一个较短的hq且{候较大的响应Ӟ相关q蝲与传输的数据总量相比׃比较低,而且Q如果请求立卛_出那么响应时间也会快一些。以上操作可以通过讄套接字的TCP_NODELAY选项来完成,q样q用了 Nagle法?
另外一U情况则需要我们等到数据量辑ֈ最大时才通过|络一ơ发送全部数据,q种数据传输方式有益于大量数据的通信性能Q典型的应用是文g服务器。应用Nagle法在这U情况下׃产生问题。但是,如果你正在发送大量数据,你可以设|TCP_CORK选项用Nagle化,其方式正好同 TCP_NODELAY相反QTCP_CORK ?TCP_NODELAY 是互相排斥的Q。下面就让我们仔l分析下其工作原理?
假设应用E序使用sendfile()函数来{Ud量数据。应用协议通常要求发送某些信息来预先解释数据Q这些信息其实就是报头内宏V典型情况下报头很小Q而且套接字上讄了TCP_NODELAY。有报头的包被立即传输Q在某些情况下(取决于内部的包计数器Q,因ؓq个包成功地被对Ҏ到后需要请求对方确认。这P大量数据的传输就会被推迟而且产生了不必要的网l流量交换?
但是Q如果我们在套接字上讄了TCP_CORKQ可以比Mؓ在管道上插入“塞子”Q选项Q具有报头的包就会填补大量的数据Q所有的数据都根据大自动地通过包传输出厅R当数据传输完成Ӟ最好取消TCP_CORK 选项讄l连?#8220;拔去塞子”以便M部分的都能发送出厅R这?#8220;塞住”|络q接同等重要?
总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文)Q那么我们徏议你讄TCP_CORK选项Q这样在q些数据之间不存在gq。能极大地有益于WWW、FTP以及文g服务器的性能Q同时也化了你的工作?
转自Q?br>http://blog.chinaunix.net/u2/76292/showart.php?id=2105375