锘??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");
from錛?/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.
瀛楃璁懼榪樻槸鍧楄澶囩殑瀹氫箟灞炰簬鎿嶄綔緋葷粺鐨勮澶囪闂眰錛屼笌瀹為檯鐗╃悊璁懼鐨勭壒鎬ф棤蹇呯劧鑱旂郴銆?/p>
璁懼璁塊棶灞備笅闈㈡槸椹卞姩紼嬪簭錛屾墍浠ュ彧瑕侀┍鍔ㄧ▼搴忔彁渚涚殑鏂瑰紡錛岄兘鍙互銆備篃灝辨槸璇撮┍鍔ㄧ▼搴忔敮鎸乻tream鏂瑰紡錛岄偅涔堝氨鍙互鐢ㄨ繖縐嶆柟寮忚闂紝椹卞姩紼嬪簭濡傛灉榪樻敮鎸乥lock鏂瑰紡錛岄偅涔堜綘鎯崇敤鍝鏂瑰紡璁塊棶閮藉彲浠ワ紝鍏稿瀷鐨勬瘮濡傜‖鐩樺紡鐨勮8璁懼錛屼袱縐嶉兘鏀寔鍧楄澶囷紙block device錛夛細鏄竴縐嶅叿鏈変竴瀹氱粨鏋勭殑闅忔満瀛樺彇璁懼錛屽榪欑璁懼鐨勮鍐欐槸鎸夊潡榪涜鐨勶紝浠栦嬌鐢ㄧ紦鍐插尯鏉ュ瓨鏀炬殏鏃剁殑鏁版嵁錛屽緟鏉′歡鎴愮啛鍚庯紝浠庣紦瀛樹竴嬈℃у啓鍏ヨ澶囨垨浠庤澶囦腑涓嬈℃ц鍑烘斁鍏ュ埌緙撳啿鍖猴紝濡傜鐩樺拰鏂囦歡緋葷粺絳?/p>
瀛楃璁懼錛圕haracter device錛夛細榪欐槸涓涓『搴忕殑鏁版嵁嫻佽澶囷紝瀵硅繖縐嶈澶囩殑璇誨啓鏄寜瀛楃榪涜鐨勶紝鑰屼笖榪欎簺瀛楃鏄繛緇湴褰㈡垚涓涓暟鎹祦銆備粬涓嶅叿澶囩紦鍐插尯錛屾墍浠ュ榪欑璁懼鐨勮鍐欐槸瀹炴椂鐨勶紝濡傜粓绔佺甯︽満絳夈?br>緋葷粺涓兘澶熼殢鏈猴紙涓嶉渶瑕佹寜欏哄簭錛夎闂浐瀹氬ぇ灝忔暟鎹墖錛坈hunks錛夌殑璁懼琚О浣滃潡璁懼錛岃繖浜涙暟鎹墖灝辯О浣滃潡銆傛渶甯歌鐨勫潡璁懼鏄‖鐩橈紝闄ゆ浠ュ錛岃繕鏈夎蔣鐩橀┍鍔ㄥ櫒銆丆D-ROM椹卞姩鍣ㄥ拰闂瓨絳夌瓑璁稿鍏朵粬鍧楄澶囥傛敞鎰忥紝瀹冧滑閮芥槸浠ュ畨瑁呮枃浠剁郴緇熺殑鏂瑰紡浣跨敤鐨勨斺旇繖涔熸槸鍧楄澶囦竴鑸殑璁塊棶鏂瑰紡銆?/p>
鍙︿竴縐嶅熀鏈殑璁懼綾誨瀷鏄瓧絎﹁澶囥傚瓧絎﹁澶囨寜鐓у瓧絎︽祦鐨勬柟寮忚鏈夊簭璁塊棶錛屽儚涓插彛鍜岄敭鐩樺氨閮藉睘浜庡瓧絎﹁澶囥傚鏋滀竴涓‖浠惰澶囨槸浠ュ瓧絎︽祦鐨勬柟寮忚璁塊棶鐨勮瘽錛岄偅灝卞簲璇ュ皢瀹冨綊浜庡瓧絎﹁澶囷紱鍙嶈繃鏉ワ紝濡傛灉涓涓澶囨槸闅忔満錛堟棤搴忕殑錛夎闂殑錛岄偅涔堝畠?yōu)灞炰簬鍧楄畱证囥?/p>
榪欎袱縐嶇被鍨嬬殑璁懼鐨勬牴鏈尯鍒湪浜庡畠浠槸鍚﹀彲浠ヨ闅忔満璁塊棶鈥斺旀崲鍙ヨ瘽璇村氨鏄紝鑳藉惁鍦ㄨ闂澶囨椂闅忔剰鍦頒粠涓涓綅緗煩杞埌鍙︿竴涓綅緗備婦涓緥瀛愶紝閿洏榪欑璁懼鎻愪緵鐨勫氨鏄竴涓暟鎹祦錛屽綋浣犳暡鍏?#8220;fox”榪欎釜瀛楃涓叉椂錛岄敭鐩橀┍鍔ㄧ▼搴忎細鎸夌収鍜岃緭鍏ュ畬鍏ㄧ浉鍚岀殑欏哄簭榪斿洖榪欎釜鐢變笁涓瓧絎︾粍鎴愮殑鏁版嵁嫻併傚鏋滆閿洏椹卞姩紼嬪簭鎵撲貢欏哄簭鏉ヨ瀛楃涓詫紝鎴栬鍙栧叾浠栧瓧絎︼紝閮芥槸娌℃湁鎰忎箟鐨勩傛墍浠ラ敭鐩樺氨鏄竴縐嶅吀鍨嬬殑瀛楃璁懼錛屽畠鎻愪緵鐨勫氨鏄敤鎴蜂粠閿洏杈撳叆鐨勫瓧絎︽祦銆傚閿洏榪涜璇繪搷浣滀細寰楀埌涓涓瓧絎︽祦錛岄鍏堟槸“f”錛岀劧鍚庢槸“o”錛屾渶鍚庢槸“x”錛屾渶緇堟槸鏂囦歡鐨勭粨鏉?EOF)銆傚綋娌′漢鏁查敭鐩樻椂錛屽瓧絎︽祦灝辨槸絀虹殑銆傜‖鐩樿澶囩殑鎯呭喌灝變笉澶т竴鏍蜂簡銆傜‖鐩樿澶囩殑椹卞姩鍙兘瑕佹眰璇誨彇紓佺洏涓婁換鎰忓潡鐨勫唴瀹癸紝鐒跺悗鍙堣漿鍘昏鍙栧埆鐨勫潡鐨勫唴瀹癸紝鑰岃璇誨彇鐨勫潡鍦ㄧ鐩樹笂浣嶇疆涓嶄竴瀹氳榪炵畫錛屾墍浠ヨ紜洏鍙互琚殢鏈鴻闂紝鑰屼笉鏄互嫻佺殑鏂瑰紡琚闂紝鏄劇劧瀹冩槸涓涓潡璁懼銆?/p>
鍐呮牳綆$悊鍧楄澶囪姣旂鐞嗗瓧絎﹁澶囩粏鑷村緱澶氾紝闇瑕佽冭檻鐨勯棶棰樺拰瀹屾垚鐨勫伐浣滅浉姣斿瓧絎﹁澶囨潵璇磋澶嶆潅璁稿銆傝繖鏄洜涓哄瓧絎﹁澶囦粎浠呴渶瑕佹帶鍒朵竴涓綅緗斿綋鍓嶄綅緗旇屽潡璁懼璁塊棶鐨勪綅緗繀欏昏兘澶熷湪浠嬭川鐨勪笉鍚屽尯闂村墠鍚庣Щ鍔ㄣ傛墍浠ヤ簨瀹炰笂鍐呮牳涓嶅繀鎻愪緵涓涓笓闂ㄧ殑瀛愮郴緇熸潵綆$悊瀛楃璁懼錛屼絾鏄鍧楄澶囩殑綆$悊鍗村繀欏昏鏈変竴涓笓闂ㄧ殑鎻愪緵鏈嶅姟鐨勫瓙緋葷粺銆備笉浠呬粎鏄洜涓哄潡璁懼鐨勫鏉傛ц繙榪滈珮浜庡瓧絎﹁澶囷紝鏇撮噸瑕佺殑鍘熷洜鏄潡璁懼瀵規(guī)墽琛屾ц兘鐨勮姹傚緢楂橈紱瀵圭‖鐩樻瘡澶氫竴鍒嗗埄鐢ㄩ兘浼氬鏁翠釜緋葷粺鐨勬ц兘甯︽潵鎻愬崌錛屽叾鏁堟灉瑕佽繙榪滄瘮閿洏鍚炲悙閫熷害鎴愬嶇殑鎻愰珮澶у緱澶氥傚彟澶栵紝鎴戜滑灝嗕細鐪嬪埌錛屽潡璁懼鐨勫鏉傛т細涓鴻繖縐嶄紭鍖栫暀涓嬪緢澶х殑鏂藉睍絀洪棿.
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,涓轟簡鏈浼樿鍙栫鐩樻暟鎹?buff澶у皬璁句負4096瀛楄妭.浣嗗湪嫻嬭瘯涓彂鐜拌涓?024鎴?192涓嶄細瀵逛紶杈撻熷害甯︽潵褰卞搷.
鏂囦歡澶у皬:9M; 鑰楁椂:0.71 - 0.76縐?
鏂囦歡澶у皬:32M; 鑰楁椂:2.64 - 2.68縐?
鏂囦歡澶у皬:64M; 鑰楁椂:5.36 - 5.43縐?
B. 浣跨敤sendfile()浼犺緭浠g爜孌?
|
off_t offset = 0; fd = open(FILENAME, O_RDONLY); |
鏂囦歡澶у皬:9M; 鑰楁椂:0.71 - 1.08縐?
鏂囦歡澶у皬:32M; 鑰楁椂:2.66 - 2.74縐?
鏂囦歡澶у皬:64M; 鑰楁椂:5.43 - 6.64縐?
浼間箮榪樼暐鏈変笅闄?鏍規(guī)嵁sendfile鐨刴an鎵嬪唽,鎴戝湪浣跨敤璇ュ嚱鏁板墠璋冪敤浜?br>
|
int no = 1; |
鏂囦歡澶у皬:9M; 鑰楁椂:0.72 - 0.75縐?
鏂囦歡澶у皬:32M; 鑰楁椂:2.66 - 2.68縐?
鏂囦歡澶у皬:64M; 鑰楁椂:5.38 - 5.60縐?
榪欐牱浼間箮杈懼埌浜嗕紶緇熸柟寮忕殑閫熷害?!涓嶇鍝鐜涓?鎴戠敤ethereal鎶撳寘鏄劇ず姣忎竴涓猼cp鍖呯殑playload閮ㄥ垎鏈澶т篃閫氬父鏄?448瀛楄妭.
鐪嬫潵鎴戠殑嫻嬭瘯娌℃湁浣撶幇鍑?搴旂敤灞傛暟鎹殑涓ゆ鎷瘋礉甯︽潵寰堝ぇ鐨勬秷鑰?榪欎竴璇存硶.濡傛灉鎸夌収瀛樺湪灝辨槸鏈夌悊鐨勮娉曠殑璇?閭f垜鎯硈endfile()鍦ㄤ袱縐嶆儏鍐典笅鎵嶄綋鐜頒紭鍔?浣嗘垜鍗存病鏈夌幆澧冩祴璇?
1. 澶у茍鍙戦噺鐨勬枃浠舵湇鍔″櫒鎴朒TTP鏈嶅姟鍣?
2. 鍐呭瓨璧勬簮绱у紶鐨勫祵鍏ュ紡緋葷粺;
鍙﹀,緗戠粶涓婂ぇ閲忕殑鍏充簬tcp閫夐」涓殑TCP_CORK鎻忚堪宸茬粡榪囨椂.鍦╩an鎵嬪唽涓棭宸叉彁鍒拌鍙傛暟鍙互涓嶵CP_NODELAY緇撳悎浣跨敤浜?鍙槸,鍙璁劇疆浜員CP_NODELAY閫夐」鍚?涓嶇鏄惁璁劇疆TCP_CORK,鍖呴兘浼氱珛鍗沖彂鍑?
----------------------------------------------------------------------
琛ュ厖:
TCP_NODELAY鍜孴CP_CORK鍩烘湰涓婃帶鍒朵簡鍖呯殑“Nagle鍖?#8221;錛孨agle鍖栧湪榪欓噷鐨勫惈涔夋槸閲囩敤Nagle綆楁硶鎶婅緝?yōu)畯鐨勫寘缁勮湄撴洿澶х殑鍝徙?John Nagle鏄疦agle綆楁硶鐨勫彂鏄庝漢錛屽悗鑰呭氨鏄敤浠栫殑鍚嶅瓧鏉ュ懡鍚嶇殑錛屼粬鍦?984騫撮嬈$敤榪欑鏂規(guī)硶鏉ュ皾璇曡В鍐崇鐗規(guī)苯杞﹀叕鍙哥殑緗戠粶鎷ュ闂錛堟浜嗚В璇︽儏璇峰弬鐪婭ETF RFC 896錛夈備粬瑙e喅鐨勯棶棰樺氨鏄墍璋撶殑silly window syndrome 錛屼腑鏂囩О“鎰氳牏紿楀彛鐥囧欑兢”錛屽叿浣撳惈涔夋槸錛屽洜涓烘櫘閬嶇粓绔簲鐢ㄧ▼搴忔瘡浜х敓涓嬈″嚮閿搷浣滃氨浼氬彂閫佷竴涓寘錛岃屽吀鍨嬫儏鍐典笅涓涓寘浼氭嫢鏈変竴涓瓧鑺傜殑鏁版嵁杞借嵎浠ュ強40涓瓧鑺傞暱鐨勫寘澶達紝浜庢槸浜х敓4000%鐨勮繃杞斤紝寰堣交鏄撳湴灝辮兘浠ょ綉緇滃彂鐢熸嫢濉?銆?Nagle鍖栧悗鏉ユ垚浜嗕竴縐嶆爣鍑嗗茍涓旂珛鍗沖湪鍥犵壒緗戜笂寰椾互瀹炵幇銆傚畠鐜板湪宸茬粡鎴愪負緙虹渷閰嶇疆浜嗭紝浣嗗湪鎴戜滑鐪嬫潵錛屾湁浜涘満鍚堜笅鎶婅繖涓閫夐」鍏蟲帀涔熸槸鍚堜箮闇瑕佺殑銆?
鐜板湪璁╂垜浠亣璁炬煇涓簲鐢ㄧ▼搴忓彂鍑轟簡涓涓姹傦紝甯屾湜鍙戦佸皬鍧楁暟鎹傛垜浠彲浠ラ夋嫨绔嬪嵆鍙戦佹暟鎹垨鑰呯瓑寰呬駭鐢熸洿澶氱殑鏁版嵁鐒跺悗鍐嶄竴嬈″彂閫佷袱縐嶇瓥鐣ャ傚鏋滄垜浠┈涓婂彂閫佹暟鎹紝閭d箞浜や簰鎬х殑浠ュ強瀹㈡埛/鏈嶅姟鍣ㄥ瀷鐨勫簲鐢ㄧ▼搴忓皢鏋佸ぇ鍦板彈鐩娿備緥濡傦紝褰撴垜浠鍦ㄥ彂閫佷竴涓緝鐭殑璇鋒眰騫朵笖絳夊欒緝澶х殑鍝嶅簲鏃訛紝鐩稿叧榪囪澆涓庝紶杈撶殑鏁版嵁鎬婚噺鐩告瘮灝變細姣旇緝浣庯紝鑰屼笖錛屽鏋滆姹傜珛鍗沖彂鍑洪偅涔堝搷搴旀椂闂翠篃浼氬揩涓浜涖備互涓婃搷浣滃彲浠ラ氳繃璁劇疆濂楁帴瀛楃殑TCP_NODELAY閫夐」鏉ュ畬鎴愶紝榪欐牱灝辯鐢ㄤ簡 Nagle綆楁硶銆?
鍙﹀涓縐嶆儏鍐靛垯闇瑕佹垜浠瓑鍒版暟鎹噺杈懼埌鏈澶ф椂鎵嶉氳繃緗戠粶涓嬈″彂閫佸叏閮ㄦ暟鎹紝榪欑鏁版嵁浼犺緭鏂瑰紡鏈夌泭浜庡ぇ閲忔暟鎹殑閫氫俊鎬ц兘錛屽吀鍨嬬殑搴旂敤灝辨槸鏂囦歡鏈嶅姟鍣ㄣ傚簲鐢∟agle綆楁硶鍦ㄨ繖縐嶆儏鍐典笅灝變細浜х敓闂銆備絾鏄紝濡傛灉浣犳鍦ㄥ彂閫佸ぇ閲忔暟鎹紝浣犲彲浠ヨ緗甌CP_CORK閫夐」紱佺敤Nagle鍖栵紝鍏舵柟寮忔濂藉悓 TCP_NODELAY鐩稿弽錛圱CP_CORK 鍜?TCP_NODELAY 鏄簰鐩告帓鏂ョ殑錛夈備笅闈㈠氨璁╂垜浠粩緇嗗垎鏋愪笅鍏跺伐浣滃師鐞嗐?
鍋囪搴旂敤紼嬪簭浣跨敤sendfile()鍑芥暟鏉ヨ漿縐誨ぇ閲忔暟鎹傚簲鐢ㄥ崗璁氬父瑕佹眰鍙戦佹煇浜涗俊鎭潵棰勫厛瑙i噴鏁版嵁錛岃繖浜涗俊鎭叾瀹炲氨鏄姤澶村唴瀹廣傚吀鍨嬫儏鍐典笅鎶ュご寰堝皬錛岃屼笖濂楁帴瀛椾笂璁劇疆浜員CP_NODELAY銆傛湁鎶ュご鐨勫寘灝嗚绔嬪嵆浼犺緭錛屽湪鏌愪簺鎯呭喌涓嬶紙鍙栧喅浜庡唴閮ㄧ殑鍖呰鏁板櫒錛夛紝鍥犱負榪欎釜鍖呮垚鍔熷湴琚鏂規(guī)敹鍒板悗闇瑕佽姹傚鏂圭‘璁ゃ傝繖鏍鳳紝澶ч噺鏁版嵁鐨勪紶杈撳氨浼氳鎺ㄨ繜鑰屼笖浜х敓浜嗕笉蹇呰鐨勭綉緇滄祦閲忎氦鎹€?
浣嗘槸錛屽鏋滄垜浠湪濂楁帴瀛椾笂璁劇疆浜員CP_CORK錛堝彲浠ユ瘮鍠諱負鍦ㄧ閬撲笂鎻掑叆“濉炲瓙”錛夐夐」錛屽叿鏈夋姤澶寸殑鍖呭氨浼氬~琛ュぇ閲忕殑鏁版嵁錛屾墍鏈夌殑鏁版嵁閮芥牴鎹ぇ灝忚嚜鍔ㄥ湴閫氳繃鍖呬紶杈撳嚭鍘匯傚綋鏁版嵁浼犺緭瀹屾垚鏃訛紝鏈濂藉彇娑圱CP_CORK 閫夐」璁劇疆緇欒繛鎺?#8220;鎷斿幓濉炲瓙”浠ヤ究浠諱竴閮ㄥ垎鐨勫撫閮借兘鍙戦佸嚭鍘匯傝繖鍚?#8220;濉炰綇”緗戠粶榪炴帴鍚岀瓑閲嶈銆?
鎬昏岃█涔嬶紝濡傛灉浣犺偗瀹氳兘涓璧峰彂閫佸涓暟鎹泦鍚堬紙渚嬪HTTP鍝嶅簲鐨勫ご鍜屾鏂囷級錛岄偅涔堟垜浠緩璁綘璁劇疆TCP_CORK閫夐」錛岃繖鏍峰湪榪欎簺鏁版嵁涔嬮棿涓嶅瓨鍦ㄥ歡榪熴傝兘鏋佸ぇ鍦版湁鐩婁簬WWW銆丗TP浠ュ強鏂囦歡鏈嶅姟鍣ㄧ殑鎬ц兘錛屽悓鏃朵篃綆鍖栦簡浣犵殑宸ヤ綔銆?
杞嚜錛?br>http://blog.chinaunix.net/u2/76292/showart.php?id=2105375