??xml version="1.0" encoding="utf-8" standalone="yes"?>99国产欧美精品久久久蜜芽,亚洲精品成人网久久久久久,亚洲国产精品无码久久九九http://www.shnenglu.com/beautykingdom/category/12530.htmlzh-cnTue, 13 Sep 2011 16:44:03 GMTTue, 13 Sep 2011 16:44:03 GMT60ubuntu下编译内?/title><link>http://www.shnenglu.com/beautykingdom/archive/2011/09/14/155714.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Tue, 13 Sep 2011 16:02:00 GMT</pubDate><guid>http://www.shnenglu.com/beautykingdom/archive/2011/09/14/155714.html</guid><wfw:comment>http://www.shnenglu.com/beautykingdom/comments/155714.html</wfw:comment><comments>http://www.shnenglu.com/beautykingdom/archive/2011/09/14/155714.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/beautykingdom/comments/commentRss/155714.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/beautykingdom/services/trackbacks/155714.html</trackback:ping><description><![CDATA[<div>先make menuconfigQ选定cpu型号Q要不会在install内核q启的时候出现cpu unsupported之类的错。具体的命o为:<br /><span style="widows: 2; text-transform: none; background-color: rgb(250,250,250); text-indent: 0px; letter-spacing: normal; font: 12px/16px Monaco, 'Courier New', monospace; white-space: normal; orphans: 2; color: rgb(0,102,0); word-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" class="Apple-style-span">sudo make-kpkg --initrd --append-to-version=dell1400 kernel_image kernel-headers<br /><span style="widows: 2; text-transform: none; background-color: rgb(250,250,250); text-indent: 0px; letter-spacing: normal; font: 12px/16px Monaco, 'Courier New', monospace; white-space: normal; orphans: 2; color: rgb(0,102,0); word-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" class="Apple-style-span">sudo dpkg -i   linux-image-*.deb<br /><br /><br />references:<br />1.<a >http://forum.ubuntu.org.cn/viewtopic.php?t=134404</a></span></span></div><img src ="http://www.shnenglu.com/beautykingdom/aggbug/155714.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/beautykingdom/" target="_blank">chatler</a> 2011-09-14 00:02 <a href="http://www.shnenglu.com/beautykingdom/archive/2011/09/14/155714.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title><?gt;how to start a kernel threadhttp://www.shnenglu.com/beautykingdom/archive/2011/03/22/142512.htmlchatlerchatlerTue, 22 Mar 2011 13:08:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2011/03/22/142512.htmlhttp://www.shnenglu.com/beautykingdom/comments/142512.htmlhttp://www.shnenglu.com/beautykingdom/archive/2011/03/22/142512.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/142512.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/142512.htmlLinux Kernel Threads in Device Drivers 
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(&current->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




chatler 2011-03-22 21:08 发表评论
]]>
The Linux Kernel Module Programming Guidehttp://www.shnenglu.com/beautykingdom/archive/2010/11/29/134974.htmlchatlerchatlerMon, 29 Nov 2010 04:03:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/11/29/134974.htmlhttp://www.shnenglu.com/beautykingdom/comments/134974.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/11/29/134974.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/134974.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/134974.html阅读全文

chatler 2010-11-29 12:03 发表评论
]]>
A Beast of a Different Naturehttp://www.shnenglu.com/beautykingdom/archive/2010/05/22/116129.htmlchatlerchatlerSat, 22 May 2010 13:09:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/05/22/116129.htmlhttp://www.shnenglu.com/beautykingdom/comments/116129.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/05/22/116129.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/116129.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/116129.htmllinux kernel development-chapter 2 getting started with the kernel 

A Beast of a Different Nature

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.

No libc

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.

Header Files

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.


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().

GNU C

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.

Inline Functions

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.

Inline Assembly

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.

Branch Annotation

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.

No Memory Protection

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!

No (Easy) Use of Floating Point

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.

Small, Fixed-Size Stack

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.

Synchronization and Concurrency

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.

Portability Is Important

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.




chatler 2010-05-22 21:09 发表评论
]]>
HOWTO compile kernel modules for the kernel 2.6http://www.shnenglu.com/beautykingdom/archive/2010/04/14/112591.htmlchatlerchatlerWed, 14 Apr 2010 15:00:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/04/14/112591.htmlhttp://www.shnenglu.com/beautykingdom/comments/112591.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/04/14/112591.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/112591.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/112591.html

If you want to compile the sum-module (source mirrored below), follow these steps:

Create the Makefile in your directory with the sum-module.c

 obj-m    := sum-module.o

KDIR    := /lib/modules/$(shell uname -r)/build

PWD    := $(shell pwd)

default:

       $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

Now do a

 make

... and the sum-module.ko is built.

 If you get something like this

# make

make: 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 until
 all "HOSTCC scripts/" stuff is done - this will configure your kernel and allows external module compilation).
Make sure /lib/modules/$(shell uname -r)/build points to your build directory (most likely /usr/src/linux...).

Another 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/sh

install -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/include

CFLAGS      := -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;


        sum += val;     /* mod 2^64 */

        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


chatler 2010-04-14 23:00 发表评论
]]>
Linux内核中的一些基本编E操?/title><link>http://www.shnenglu.com/beautykingdom/archive/2010/04/01/111316.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Thu, 01 Apr 2010 11:59:00 GMT</pubDate><guid>http://www.shnenglu.com/beautykingdom/archive/2010/04/01/111316.html</guid><wfw:comment>http://www.shnenglu.com/beautykingdom/comments/111316.html</wfw:comment><comments>http://www.shnenglu.com/beautykingdom/archive/2010/04/01/111316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/beautykingdom/comments/commentRss/111316.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/beautykingdom/services/trackbacks/111316.html</trackback:ping><description><![CDATA[<div>本文档的Copyleft归yfydz所有,使用GPL发布Q可以自由拷贝,转蝲Q{载时请保持文档的完整性,严禁用于M商业用途?br>msn: <a href="mailto:yfydz_no1@hotmail.com"><u><font color="#0000ff">yfydz_no1@hotmail.com</font></u></a><br>来源Q?a ><u><font color="#0000ff">http://yfydz.cublog.cn</font></u></a></div> <div><br>1. 前言</div> <div> </div> <div>本文介绍linux内核中一些常用的数据l构和操作?/div> <div> </div> <div>2. 双向链表(list)</div> <div> </div> <div>linux内核中的双向链表通过l构 struct list_head来将各个节点q接hQ此l构会作为链表元素结构中的一个参敎ͼ</div> <div>struct list_head {<br> struct list_head *next, *prev;<br>};</div> <div> </div> <div>链表头的初始化,注意Q结构中的指针ؓNULLq不是初始化Q而是指向自n才是初始化,如果只是按普通情况下的置为NULLQ而不是指向自w,pȝ会崩溃,q是一个容易犯的错误:</div> <div> </div> <div>#define LIST_HEAD_INIT(name) { &(name), &(name) }</div> <div>#define LIST_HEAD(name) \<br> struct list_head name = LIST_HEAD_INIT(name)</div> <div>#define INIT_LIST_HEAD(ptr) do { \<br> (ptr)->next = (ptr); (ptr)->prev = (ptr); \<br>} while (0)</div> <div> </div> <div>最常用的链表操作:</div> <div>插入到链表头:<br>void list_add(struct list_head *new, struct list_head *head);</div> <div> </div> <div>插入到链表尾:<br>void list_add_tail(struct list_head *new, struct list_head *head);</div> <div> </div> <div>删除链表节点:<br>void list_del(struct list_head *entry);</div> <div> </div> <div>节点移动到另一链表:<br>void list_move(struct list_head *list, struct list_head *head);</div> <div> </div> <div>节点移动到链表?<br>void list_move_tail(struct list_head *list,struct list_head *head);</div> <div> </div> <div>判断链表是否为空Q返?为空Q?非空<br>int list_empty(struct list_head *head);</div> <div> </div> <div>把两个链表拼接v来:<br>void list_splice(struct list_head *list, struct list_head *head)Q?/div> <div> </div> <div>取得节点指针Q?br>#define list_entry(ptr, type, member) \<br> ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))</div> <div> </div> <div>遍历链表中每个节点:<br>#define list_for_each(pos, head) \<br> for (pos = (head)->next, prefetch(pos->next); pos != (head); \<br>         pos = pos->next, prefetch(pos->next))</div> <div> </div> <div>逆向循环链表中每个节点:<br>#define list_for_each_prev(pos, head) \<br> for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \<br>         pos = pos->prev, prefetch(pos->prev))</div> <div> </div> <div>举例Q?/div> <div> </div> <div>LISH_HEAD(mylist);</div> <div> </div> <div>struct my_list{<br> struct list_head list;<br> int data;<br>};</div> <div> </div> <div>static int ini_list(void)<br>{<br> struct my_list *p;<br> int i;<br> for(i=0; i<100; i++){<br>  p=kmalloc(sizeof(struct my_list), GFP_KERNEL);<br>  list_add(&p->list, &mylist);<br> }<br>}</div> <div><br>在内存中形成如下l构的一个双向链表:</div> <div> </div> <div>  +---------------------------------------------------------------+<br>  |                                                               |<br>  |  mylist         99            98                     0        |<br>  |  +----+    +---------+    +---------+           +---------+   |<br>  +->|next|--->|list.next|--->|list.next|--->...--->|list.next|---+<br>     |----|    |---------|    |---------|           |---------|<br>  +--|prev|<---|list.prev|<---|list.prev|<---...<---|list.prev|<--+<br>  |  +----+    |---------|    |---------|           |---------|   |<br>  |            |  data   |    |  data   |           |  data   |   |<br>  |            +---------+    +---------+           +---------+   |<br>  |                                                               |<br>  +---------------------------------------------------------------+</div> <div> </div> <div>知道了链表头p遍历整个链表Q如果是用list_add()插入新节点的话,从链表头的next方向看是一个堆栈型?/div> <div> </div> <div>从链表中删除节点很容易:</div> <div>static void del_item(struct my_list *p)<br>{<br> list_del(&p->list, &mylist);<br> kfree(p);<br>}</div> <div> </div> <div>最重要的宏是list_entryQ这个宏的思\是根据链表元素结构中链表头结构list_head的地址推算出链表元素结构的实际地址Q?/div> <div> </div> <div>#define list_entry(ptr, type, member) \<br> ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))</div> <div> </div> <div>ptr是链表元素结?如struct my_list)中链表头l构list_head的地址<br>member是链表元素结?如struct my_list)中链表头l构list_head参数的名U?br>type是链表元素结构类?如struct my_list)<br></div> <div>计算原理是根据链表头l构list_head的地址减去其在链表元素l构中的偏移位置而得到链表元素结构的地址?/div> <div> </div> <div>例如Q?/div> <div>static void print_list(void)<br>{<br> struct list_head *cur;<br> struct my_list *p;</div> <div> list_for_each(cur, &mylist){<br>  p=list_entry(cur, struct my_list, list);<br>  printk("data=%d\n", p->data);<br> }<br>}</div> <div> </div> <div>优点Q?br></div> <div>q样可以用相同的数据处理方式来描述所有双向链表,不用再单独ؓ各个链表~写各种~辑函数?/div> <div> </div> <div>~点Q?br>1) 链表头中元素|ؓNULL不是初始化,与普通习惯不同;<br>2) 仍然需要单独编写各自的删除整个链表的函敎ͼ不能l一处理Q因Z能保证所有链表元素结构中链表头结构list_head的偏Ud址都是相同的,当然如果把链表头l构list_head都作为链表元素结构的W一个参敎ͼ可以用l一的删除整个链表的函数?/div> <div><br>3. HASH?/div> <div> </div> <div>HASH表适用于不需要对整个I间元素q行排序Q而是只需要能快速找到某个元素的场合Q是一U以I间换时间的ҎQ本质也是线性表Q但׃个大的线性表拆分Z多个线性表Q由于只需要查扑ְ表,因此搜烦速度׃U性查整个大表提高很多Q理x况下Q有多少个小U性表Q搜索速度提高了多少倍,通常把小U性表的表头综合ؓ一个数l,大小是HASH表的数量?/div> <div> </div> <div>HASH表速度的关键是HASH函数的设计,HASH函数Ҏ每个元素中固定的参数q行计算Q算Z个不大于HASH表数量的索引|表示该元素需要放在该索引号对应的那个表中Q对于固定的参数Q计结果始l是固定的,但对于不同的参数|希望计算出来的结果能可能地q_到每个烦引|HASH函数计算得越q_Q表C每个小表中元素的数量都会差不多Q这h索性能越好?span style="color: red;">HASH函数也要可能的单,以减计时_常用的算法是参数篏加求?/span>Q在include/linux/jhash.h中已l定义了一些HASH计算函数Q可直接使用?/div> <div> </div> <div style="color: red;">HASH表在路由cache表,状态连接表{处用得很多?/div> <div> </div> <div>举例Q连接跟t中ҎtupleDHASHQ?/div> <div>// net/ipv4/netfilter/ip_conntrack_core.c</div> <div>u_int32_t<br>hash_conntrack(const struct ip_conntrack_tuple *tuple)<br>{<br>#if 0<br> dump_tuple(tuple);<br>#endif<br> return (jhash_3words(tuple->src.ip,<br>                      (tuple->dst.ip ^ tuple->dst.protonum),<br>                      (tuple->src.u.all | (tuple->dst.u.all << 16)),<br>                      ip_conntrack_hash_rnd) % ip_conntrack_htable_size);<br>}</div> <div> </div> <div>// include/linux/jhash.h<br>static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)<br>{<br> a += JHASH_GOLDEN_RATIO;<br> b += JHASH_GOLDEN_RATIO;<br> c += initval;</div> <div> __jhash_mix(a, b, c);</div> <div> return c;<br>}</div> <div> </div> <div>4. 定时?timer)</div> <div> </div> <div>linux内核定时器由以下l构描述Q?/div> <div> </div> <div>/* include/linux/timer.h */<br>struct timer_list {<br> struct list_head list;<br> unsigned long expires;<br> unsigned long data;<br> void (*function)(unsigned long);<br>};<br></div> <div>listQtimer链表<br>expiresQ到期时?br>functionQ到期函敎ͼ旉到期时调用的函数<br>dataQ传l到期函数的数据Q实际应用中通常是一个指针{化而来Q该指针指向一个结?/div> <div><br>timer的操作:</div> <div> </div> <div>增加timerQ将timer挂接到系l的timer链表Q?br>extern void add_timer(struct timer_list * timer);</div> <div> </div> <div>删除timerQ将timer从系ltimer链表中拆除:<br>extern int del_timer(struct timer_list * timer);<br>(del_timer()函数可能会失败,q是因ؓ该timer本来已经不在pȝtimer链表中了Q也是已经删除q了)</div> <div> </div> <div>对于SMPpȝQ删除timer最好用下面的函数来防止冲H:<br>extern int del_timer_sync(struct timer_list * timer);</div> <div> </div> <div>修改timerQ修改timer的到期时_<br>int mod_timer(struct timer_list *timer, unsigned long expires);</div> <div> </div> <div>通常用法Q?br><span style="color: red;">    struct timer_list通常作ؓ数据l构中的一个参敎ͼ在初始化l构的时候初始化timerQ表C到期时要进行的操作Q实现定时动作,通常更多的是作ؓ时处理的,timer函数作ؓ时时的资源释放函数。注意:如果时了运行超时函敎ͼ此时pȝ是处在时钟中断的bottom half里的Q不能进行很复杂的操作,如果要完成一些复杂操作,如到期后的数据发送,不能直接在到期函C处理Q而是应该在到期函C发个信号l特定内核线E{到top halfq行处理?/span></div> <div> </div> <div>为判断时间的先后Q内怸定义了以下宏来判断:</div> <div>#define time_after(a,b)  ((long)(b) - (long)(a) < 0)<br>#define time_before(a,b) time_after(b,a)</div> <div>#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)<br>#define time_before_eq(a,b) time_after_eq(b,a)</div> <div>q里用到了一个技巧,<span style="color: red;">׃linux中的旉是无W号敎ͼq里先将其{换ؓ有符h后再判断Q就能解x间回l问题,当然只是一ơ回l,回绕两次当然是判断不出来的,具体可自己实验体会?/span></div> <div> </div> <div>5. 内核U程(kernel_thread)</div> <div> </div> <div>内核中新U程的徏立可以用kernel_thread函数实现Q该函数在kernel/fork.c中定义:</div> <div>long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)</div> <div>fnQ内核线E主函数Q?br>argQ线E主函数的参敎ͼ<br>flagsQ徏立线E的标志Q?/div> <div> </div> <div>内核U程函数通常都调用daemonize()q行后台化作Z个独立的U程q行Q然后设|线E的一些参敎ͼ如名Uͼ信号处理{,q也不是必须的,然后p入一个死循环Q这是线E的M部分Q这个@环不能一直在q行Q否则系l就dq了Q或者是某种事g驱动的,在事件到来前是睡眠的Q事件到来后唤醒q行操作Q操作完后l睡眠;或者是定时睡眠Q醒后操作完再睡眠;或者加入等待队列通过schedule()调度获得执行旉。M是不能一直占着 CPU?/div> <div> </div> <div>以下是内核线E的一个实例,取自kernel/context.c:</div> <div> </div> <div>int start_context_thread(void)<br>{<br> static struct completion startup __initdata = COMPLETION_INITIALIZER(startup);</div> <div> kernel_thread(context_thread, &startup, CLONE_FS | CLONE_FILES);<br> wait_for_completion(&startup);<br> return 0;<br>}</div> <div>static int context_thread(void *startup)<br>{<br> struct task_struct *curtask = current;<br> DECLARE_WAITQUEUE(wait, curtask);<br> struct k_sigaction sa;</div> <div> daemonize();<br> strcpy(curtask->comm, "keventd");<br> keventd_running = 1;<br> keventd_task = curtask;</div> <div> spin_lock_irq(&curtask->sigmask_lock);<br> siginitsetinv(&curtask->blocked, sigmask(SIGCHLD));<br> recalc_sigpending(curtask);<br> spin_unlock_irq(&curtask->sigmask_lock);</div> <div> complete((struct completion *)startup);</div> <div> /* Install a handler so SIGCLD is delivered */<br> sa.sa.sa_handler = SIG_IGN;<br> sa.sa.sa_flags = 0;<br> siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));<br> do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);</div> <div> /*<br>  * If one of the functions on a task queue re-adds itself<br>  * to the task queue we call schedule() in state TASK_RUNNING<br>  */<br> for (;;) {<br>  set_task_state(curtask, TASK_INTERRUPTIBLE);<br>  add_wait_queue(&context_task_wq, &wait);<br>  if (TQ_ACTIVE(tq_context))<br>   set_task_state(curtask, TASK_RUNNING);<br>  schedule();<br>  remove_wait_queue(&context_task_wq, &wait);<br>  run_task_queue(&tq_context);<br>  wake_up(&context_task_done);<br>  if (signal_pending(curtask)) {<br>   while (waitpid(-1, (unsigned int *)0, __WALL|WNOHANG) > 0)<br>    ;<br>   spin_lock_irq(&curtask->sigmask_lock);<br>   flush_signals(curtask);<br>   recalc_sigpending(curtask);<br>   spin_unlock_irq(&curtask->sigmask_lock);<br>  }<br> }<br>}</div> <div> </div> <div>6. l构地址</div> <div> </div> <div>在C中,l构地址和结构中W一个元素的地址是相同的Q因此在linux内核中经常出C用结构第一个元素的地址来表C结构地址的情况,在读代码时要注意q一点,q和list_entry宏的意思一栗?/div> <div> </div> <div>如:<br>struct my_struct{<br> int a;<br> int b;<br>}c;</div> <div>if(&c == &c.a){  // always true<br>...<br>}<br><br><br><br>from:<br><br><a >http://blog.chinaunix.net/u/12313/showart_109612.html</a></div><img src ="http://www.shnenglu.com/beautykingdom/aggbug/111316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/beautykingdom/" target="_blank">chatler</a> 2010-04-01 19:59 <a href="http://www.shnenglu.com/beautykingdom/archive/2010/04/01/111316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何在Linux内核中写文ghttp://www.shnenglu.com/beautykingdom/archive/2010/02/27/108529.htmlchatlerchatlerSat, 27 Feb 2010 03:02:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/02/27/108529.htmlhttp://www.shnenglu.com/beautykingdom/comments/108529.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/02/27/108529.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/108529.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/108529.html#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>

#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");


fromQ?br>http://blog.csdn.net/coofive/archive/2006/05/07/712028.aspx

chatler 2010-02-27 11:02 发表评论
]]>
What is the difference between user level threads and kernel level threads?http://www.shnenglu.com/beautykingdom/archive/2010/02/27/108526.htmlchatlerchatlerSat, 27 Feb 2010 02:00:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/02/27/108526.htmlhttp://www.shnenglu.com/beautykingdom/comments/108526.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/02/27/108526.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/108526.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/108526.html

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.


from:
http://blog.csdn.net/jicheng687/archive/2009/09/08/4527676.aspx




chatler 2010-02-27 10:00 发表评论
]]>
Linux 内核W记2 ?q程调度http://www.shnenglu.com/beautykingdom/archive/2010/02/15/107892.htmlchatlerchatlerMon, 15 Feb 2010 07:30:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/02/15/107892.htmlhttp://www.shnenglu.com/beautykingdom/comments/107892.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/02/15/107892.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/107892.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/107892.html1 前言
2 调度法
2.1.1 常用概念
2.1.2 q程数据l构中相兛_?
2.1.3 调度法说明
2.1.4 相关函数
3 调度E序的执?
3.1.1 直接调用
3.1.2 延迟调用
3.1.3 相关函数
4 q程调度C意?
5 SMPpȝ的调?
6 问题与答?
7 参考文献:

1 前言
本文的许可协议遵循GNU Free Document License。协议的具体内容请参见http://www.gnu.org/copyleft/fdl.html。在遵@GNU Free Document License的基上,可以自由C播或发行本文Q但请保留本文的完整性?
Ƣ迎大家对这文章提出意见和指正Q我的email是:shisheng_liu@yahoo.com.cn?

2 调度法
2.1.1 常用概念
2.1.1.1 定时中断
通过g的可~程中断控制?254来实玎ͼ定时中断发生的频率由HZ定义Q发生的旉间隔被称为tick?
2.1.1.2 CPU节拍QtickQ?
计算机内部时间的一个计数单位,表示发生一ơ时钟中断的旉间隔?
2.1.1.3 HZ
旉中断发生的频率。在i386机器上,HZ被定义ؓ100Q因此时钟中断每10ms一ơ?
2.1.1.4 CPU时期
调度是以CPU时期为周期的Q在一个CPU时期/调度周期内,pȝ中所有程序都被执行直到用完当前的旉片,然后所有进E的counterD重新计算Qƈ开始另一个CPU时期?
2.1.1.5 q程的分c?
在调度程序看来,pȝ中的q程分ؓ两大c,分别是实时进E和普通进E。在M时候实时进E的执行都高于普通进E。进E数据结构中的policy? 员变量表CZq程是哪一c,而sched_set(/get)scheduler提供了控制进E调度policy的用L~程接口?
2.1.2 q程数据l构中相兛_?
2.1.2.1 1Qnice
q程的优先Q媄响进E获得CPU事g的多,20为最低,-19为最高?
2.1.2.2 2Qcounter
q程旉片所剩余的CPU节拍数。初始值根据进E的nice值决定,在每ơ时钟中断发生时Q也是一个CPU节拍QtickQ的时候,当前q程的counter值减1Q如果counter值变?则表C当前进E的旉片已l用完,pȝ会重新调度,军_下一个执行的q程?
2.1.2.3 3Qneed_resched
标志位。该位在从中断和pȝ调用中返回的时候被查,need_resched?的时候表C求启动调度程序,q通常发生在进E的旉片已l用完,或者因为IO事g发生而强行抢占当前进E的时候?
2.1.2.4 4Qpolicy
q程的调度策略。如果调度策略ؓSCHED_RR或者SCHED_FIFO则表C当前进Eؓ实时q程Q否?SCHED_OTHER)为普通进E?
2.1.3 调度法说明
Linux采用相当单但实际证明效果不错的调度算法。在调度的时候,所有正在运行进E的执行权?goodness)都被计算Q最l权值最高的 q程获得执行的机会。假讑־到的最大权gؓ0Q就认ؓ本次CPU时期已经执行完毕Q会重新计算所有进E的counter|开始新的CPU时期。调度算? 的核心就是goodness的计,计算的基本思\如下Q?
如果{待调度的进E是实时q程Q它的goodness?000 + 本n的优先,而普通进E的goodnessq小?000Q这׃证了实时q程L优先于普通进E执行?
如果q程剩余的counter?,p为它已经用光了自己在该时期的CPU旉片,goodnessq回0?
对于其他的情况,用下面的公式来计goodness:
goodness = counter + 20 – niceQ?
2.1.4 相关函数
1Qschedule() in kernel/sched.c
主调度函敎ͼ选择要运行的q程
2Qgoodness() in kernel/sched.c
由schedule()调用Q计进E的执行权?
3 调度E序的执?
可以通过两种方式来激z调度程序,分别是直接调用和延迟调用?
3.1.1 直接调用
当currentq程准备d攑ּCPU的时候,它会直接调用调度E序schedule()Q将CPU让给另一个进E?
促currentq程d攑ּCPU的原因有两种Q一U情冉|current需要睡眠(dQ来{待所需的资源准备好Q此时current的状 态被讄为TASK_INTERRUPTABLE或TASK_UNINTERRUPTABLEQ在调用schedule()后进E进入睡眠状态;另一U情 况下q程讄SCHED_YIELD的调度策略,然后调用schedule()Q此时进E只是短暂的攑ּCPUQ在下一ơschedule()被调用的? 候进E会l箋参与CPU的竞争?
3.1.2 延迟调用
通过讄当前q程的need_resched标志来在其后的某个时Lz调度程序。前面说q,在从中断/异常/pȝ调用中返? Ӟneed_resched标志被检查,在标志不?的时候会Ȁz调度程序。例如:当时钟中断发生时Q中断处理程序检查到当前q程的时间片已经执行? 毕,它就会设|当前进E的need_resched标志Q另一个例子是当某个IO中断发生Ӟ中断处理E序发现有进E在{待该IO事gQ它会将正在{待? q程的状态变为执行态,q设|当前进E的need_resched标志。当中断处理E序一l束Q系l会重新调度Q在q种情况下,新{入执行态的q程很可? 会获得执行机会,从而ɾpȝ保持对IO事g的快速响应?
3.1.3 相关函数
1Qwake_up_common() in kernel/sched.c
ȀzIO{待队列中的q程Q它会顺序调用try_to_wake_up()Qreschedule_idle(){函数来要求对进E进E重新调度?
2Qdo_timer() in kernel/timer.c
定时旉中断E序Q减当前进E的counter|如果counter已经用完Q则讄q程的need_resched域要求重新调度?
3Qret_from_intr/sys_call/exception in arch/i386/entry.S
汇编语言中的E序点,在从中断/异常/pȝ调用中返回时都会执行q一D늨序,查当前进E的need_resched域,如果不ؓ0׃Ȁzschedule()重新调度?


4 q程调度C意?
linux的进E调度如?所C?br> 

6 问题与答?
QQ在当前pȝ下,调度旉片的长度是多?
A. ?.2.x版的内核相比Qkernel2.4.x的时间片长度~短了,对于最高优先的进E来_旉片的长度?00msQ默认优先q程的时间片长度?0msQ而最低优先q程的时间片长度?0ms?

Q. Linux如何保证对I/O事g相对比较快的响应速度Q这个响应速度是否与调度时间片的长短有养I
AQ当I/O事g发生的是时候,对应的中断处理程序被Ȁz,当它发现有进E在{待q个I/O事g的时候,它会Ȁzȝ待进E,q且讄当前正在执行 q程的need_resched标志Q这样在中断处理E序q回的时候,调度E序被激z,原来在等待I/O事g的进E(很可能)获得执行权,从而保证了对I /O事g的相对快速响应(毫秒U)?
从上面的说明可以看出Q在I/O事g发生的时候,I/O事g的处理进E会抢占当前q程Q响应速度与调度时间片的长度无兟?

QQ高优先U?nice)q程和低优先U进E在执行上有何区别?例如一个优先?19Q最高优先Q的q程和优先?0Q最低)的进E有何区?
A. q程获得的CPU旉的绝Ҏ目取决于它的初始counter|初始的counter的计公?sched.c in kernel 2.4.14)如下Q?
p->counter = (p->counter >> 1) + ((20 - p->nice) >> 2) +1)
由公式可以计出Q对于标准进E(p->nice ?Q, 得到的初始counter?Q即q程获得的时间片?0ms?
最高优先q程Qnice?19Q的初始countergؓ10Q进E的旉片ؓ100ms?
最低优先q程Qnice?0Q的初始countergؓ1,q程旉片ؓ10ms?
l论是最高优先q程会获得最低优先q程10倍的执行旉Q普通优先q程接近两倍的执行旉。当Ӟq是在进E不q行MIO操作的时候的数据Q在有IO操作的时候,q程会经常被q睡眠来{待IO操作的完?真正所占用的CPU旉是很难比较的?
我们可以看到每次重新计算counter的时候,新的counter值都要加上它本n剩余值的一半,q种奖励只适用于通过SCHED_YIELD d攑ּCPU的进E,只有它在重新计算的时候counter值没有用完,所以在计算后counterg增大Q但永远不可能超q?0?

SMPpȝ中的调度

7 参考文献:
1Q?linux内核源代码版?.4.14
在Q何时候真实的代码L提供l我们最准确和详l的资料。感谢Linus TorvaldsQAlan Cox和其它linux开发者的辛勤力_?
2QDANIEL P.BOVET & MARCO CESATI
<<UNDERSTANDING THE LINUX KERNEL>> ISBN: 0-596-00002-2 O’REILLY 2001
中译?《深入理解Linux内核?陈莉君等?ISBN: 7-5083-0719-4 中国电力出版C?2001?
本书是专门介llinux内核l构的书中最详尽的一本,对代码分析讲解的也比较深入,Z2.2的内核版?
3QW.Richard Stevens
《UNIX环境高~程?晋元译 ISBN: 7-111-07579-X 机械工业出版C?2000
UNIX~程圣经Q程序员手头必备的书c之一,Ҏ有UNIX开发h员,无论水^高低Q都有参考h倹{翻译的水准也难得一见的高明?

from:
http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=linuxK&Number=294463&page=16&view=collapsed&sb=5&o=all&fpart=



chatler 2010-02-15 15:30 发表评论
]]>
linux块设备,字符讑֤http://www.shnenglu.com/beautykingdom/archive/2010/01/28/106630.htmlchatlerchatlerThu, 28 Jan 2010 07:00:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/01/28/106630.htmlhttp://www.shnenglu.com/beautykingdom/comments/106630.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/01/28/106630.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/106630.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/106630.html

字符讑֤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



chatler 2010-01-28 15:00 发表评论
]]>
linux内核模块理命ohttp://www.shnenglu.com/beautykingdom/archive/2009/12/10/102938.htmlchatlerchatlerThu, 10 Dec 2009 14:48:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2009/12/10/102938.htmlhttp://www.shnenglu.com/beautykingdom/comments/102938.htmlhttp://www.shnenglu.com/beautykingdom/archive/2009/12/10/102938.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/102938.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/102938.html

chatler 2009-12-10 22:48 发表评论
]]>
Linux "零拷? sendfile函数中文说明及实际操作分?/title><link>http://www.shnenglu.com/beautykingdom/archive/2009/11/28/102185.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sat, 28 Nov 2009 12:08:00 GMT</pubDate><guid>http://www.shnenglu.com/beautykingdom/archive/2009/11/28/102185.html</guid><wfw:comment>http://www.shnenglu.com/beautykingdom/comments/102185.html</wfw:comment><comments>http://www.shnenglu.com/beautykingdom/archive/2009/11/28/102185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/beautykingdom/comments/commentRss/102185.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/beautykingdom/services/trackbacks/102185.html</trackback:ping><description><![CDATA[  Sendfile函数说明 <div> </div> <div>#include <sys/sendfile.h></div> <div>ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);</div> <div> </div> <div>sendfile()是作用于数据拯在两个文件描q符之间的操作函?q个拯操作是内怸操作?所以称?零拷?.sendfile函数比vread和write函数高效得多,因ؓread和write是要把数据拷贝到用户应用层操?</div> <div> </div> <div>参数说明:</div> <div>out_fd 是已l打开?用于写操?write)的文件描q符;</div> <div>in_fd 是已l打开?用于L?read)的文件描q符;</div> <div>offset 偏移?表示sendfile函数从in_fd中的哪一偏移量开始读取数?如果是零表示从文件的开始读,否则从相应的便宜量读?如果是@环读取的时?下一ơoffset值应为sendfile函数q回值加上本ơ的offset的?</div> <div>count是在两个描述W之间拷贝的字节?bytes)</div> <div> </div> <div>q回?</div> <div>如果成功的拷?q回写操作到out_fd的字节数,错误q回-1,q相应的讄error信息.</div> <div> </div> <div>EAGAIN 无阻塞I/O讄O_NONBLOCK?写操?write)d?</div> <div>EBADF 输出或者输入的文g描述W没有打开.</div> <div>EFAULT 错误的地址.</div> <div>EINVAL 描述W不可用或者锁定了,或者用mmap()函数操作的in_fd不可?</div> <div>EIO 当读?read)in_fd时发生未知错?</div> <div>ENOMEM ?read)in_fd时内存不?</div> <div> </div> <div>------------------------------------------------------------------------------</div> <div> </div> <div> <div>׃惛_提升原有pȝ中文件传输模块的速度,q减系l资源占?q行了一ơsendfile()的性能试,但失败了.不过q是它用在了模块中.记录一下这ơ失改的微调试.</div> <div> </div> <div>q行q_: 客户Z服务器均为P4计算?IDE盘; Fedora5发行? 癑օ局域网;</div> <div> </div> <div>接收端程序如?</div> <div> <table style="font-size: 12px; width: 88.05%; height: 123px; " align="center"> <tbody> <tr> <td> <p>  FILE *fp = fopen(FILENAME,"wb");</p> <p>  while((len = recv(sockfd, buff, sizeof(buff), 0)) > 0)<br>  {<br>      fwrite(buffer, 1, len, fp);<br>  }<br>  fclose(fp);</p> </td> </tr> </tbody> </table> </div> <div> </div> <div> </div> <div>A. 发送端传统方式代码D如?</div> <div> <table style="font-size: 12px; width: 88.67%; height: 139px; " align="center"> <tbody> <tr> <td>  fd = open(FILENAME, O_RDONLY);<br>  while((len =read(fd, buff, sizeof(buff))) >0) <br>  {<br>       send(sockfd, buff, len ,0);<br>  }<br>  close(fd);  </td> </tr> </tbody> </table> </div> <p>׃我磁盘分区时指定的块大小?096,Z最优读取磁盘数?buff大小设ؓ4096字节.但在试中发现设?024?192不会对传输速度带来影响.</p> <p>文g大小:9M; 耗时:0.71 - 0.76U?<br>文g大小:32M; 耗时:2.64 - 2.68U?<br>文g大小:64M; 耗时:5.36 - 5.43U?</p> <p>B. 使用sendfile()传输代码D?<br> <table style="font-size: 12px; width: 86.6%; height: 143px; " align="center"> <tbody> <tr> <td> <p>  off_t offset = 0;<br>  stat(FILENAME, &filestat);</p> <p>  fd = open(FILENAME, O_RDONLY);<br>  sendfile(sockfd, fd, &offset, filestat.st_size) );<br>  close(fd);  </p> </td> </tr> </tbody> </table> </p> <p>文g大小:9M; 耗时:0.71 - 1.08U?<br>文g大小:32M; 耗时:2.66 - 2.74U?<br>文g大小:64M; 耗时:5.43 - 6.64U?</p> <p>gq略有下?Ҏsendfile的man手册,我在使用该函数前调用?br> <table style="font-size: 12px; width: 87.43%; height: 75px; " align="center"> <tbody> <tr> <td> <p> int no = 1;<br> printf("%d\n", setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, (char*)&no, sizeof(int)) );</p> </td> </tr> </tbody> </table> </p> <p>文g大小:9M; 耗时:0.72 - 0.75U?<br>文g大小:32M; 耗时:2.66 - 2.68U?<br>文g大小:64M; 耗时:5.38 - 5.60U?</p> <p>q样g辑ֈ了传l方式的速度?!不管哪种环境?我用ethereal抓包昄每一个tcp包的playload部分最大也通常?448字节.</p> <p>看来我的试没有体现?应用层数据的两次拯带来很大的消?q一说法.如果按照存在是有理的说法的?那我想sendfile()在两U情况下才体C?但我却没有环境测?<br>1. 大ƈ发量的文件服务器或HTTP服务?<br>2. 内存资源紧张的嵌入式pȝ;</p> <p>另外,|络上大量的关于tcp选项中的TCP_CORK描述已经q时.在man手册中早已提到该参数可以与TCP_NODELAYl合使用?只是,只要讄了TCP_NODELAY选项?不管是否讄TCP_CORK,包都会立卛_?</p> <p>----------------------------------------------------------------------</p> <p>补充:</p> <p>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也是合乎需要的?</p> <p>现在让我们假设某个应用程序发Z一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待生更多的数据然后再一ơ发送两U策略。如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当我们正在发送一个较短的hq且{候较大的响应Ӟ相关q蝲与传输的数据总量相比׃比较低,而且Q如果请求立卛_出那么响应时间也会快一些。以上操作可以通过讄套接字的TCP_NODELAY选项来完成,q样q用了 Nagle法?</p> <p>另外一U情况则需要我们等到数据量辑ֈ最大时才通过|络一ơ发送全部数据,q种数据传输方式有益于大量数据的通信性能Q典型的应用是文g服务器。应用Nagle法在这U情况下׃产生问题。但是,如果你正在发送大量数据,你可以设|TCP_CORK选项用Nagle化,其方式正好同 TCP_NODELAY相反QTCP_CORK ?TCP_NODELAY 是互相排斥的Q。下面就让我们仔l分析下其工作原理?</p> <p>假设应用E序使用sendfile()函数来{Ud量数据。应用协议通常要求发送某些信息来预先解释数据Q这些信息其实就是报头内宏V典型情况下报头很小Q而且套接字上讄了TCP_NODELAY。有报头的包被立即传输Q在某些情况下(取决于内部的包计数器Q,因ؓq个包成功地被对Ҏ到后需要请求对方确认。这P大量数据的传输就会被推迟而且产生了不必要的网l流量交换?</p> <p>但是Q如果我们在套接字上讄了TCP_CORKQ可以比Mؓ在管道上插入“塞子”Q选项Q具有报头的包就会填补大量的数据Q所有的数据都根据大自动地通过包传输出厅R当数据传输完成Ӟ最好取消TCP_CORK 选项讄l连?#8220;拔去塞子”以便M部分的都能发送出厅R这?#8220;塞住”|络q接同等重要?</p> <p>总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文)Q那么我们徏议你讄TCP_CORK选项Q这样在q些数据之间不存在gq。能极大地有益于WWW、FTP以及文g服务器的性能Q同时也化了你的工作?<br>转自Q?br><a >http://blog.chinaunix.net/u2/76292/showart.php?id=2105375</a><br></p> </div><img src ="http://www.shnenglu.com/beautykingdom/aggbug/102185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/beautykingdom/" target="_blank">chatler</a> 2009-11-28 20:08 <a href="http://www.shnenglu.com/beautykingdom/archive/2009/11/28/102185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.boyayali.cn" target="_blank">þþþƷһ</a>| <a href="http://www.globenewswire.com.cn" target="_blank">þþƷAVӰ</a>| <a href="http://www.antsgogo.cn" target="_blank">ƷɫۺϾþ</a>| <a href="http://www.xvjw.cn" target="_blank">wwwþþcom</a>| <a href="http://www.goqswequrv.cn" target="_blank">þþþһëþþ</a>| <a href="http://www.myrtv.cn" target="_blank">޹þþþƷ</a>| <a href="http://www.hbguangtao.cn" target="_blank">þ÷׾Ʒ</a>| <a href="http://www.nlzm.net.cn" target="_blank">һۺϾþ</a>| <a href="http://www.sheersky.cn" target="_blank">޹þþþƷС˵ </a>| <a href="http://www.sxzt888.cn" target="_blank">þùһ</a>| <a href="http://www.gajl.cn" target="_blank">ĻƷѾþ</a>| <a href="http://www.woai858.cn" target="_blank">þ777߿ۿƷ</a>| <a href="http://www.neang.cn" target="_blank">ij뾫Ʒþþò</a>| <a href="http://www.nsom.org.cn" target="_blank">þùƷһ</a>| <a href="http://www.hkgsjt.cn" target="_blank">ƷþþĻ</a>| <a href="http://www.cc5ujj.cn" target="_blank">ƷۺϾþþþþ97</a>| <a href="http://www.yk999.cn" target="_blank">þþþþþþþѾƷ</a>| <a href="http://www.baotou8.cn" target="_blank">պþþþþ</a>| <a href="http://www.yshome.net.cn" target="_blank">þùƷһ</a>| <a href="http://www.depsys.cn" target="_blank">þùѹۿƷ</a>| <a href="http://www.test-mts.cn" target="_blank">ŷvaþþþ</a>| <a href="http://www.akeyu.cn" target="_blank">þպƬ</a>| <a href="http://www.zhunsan.cn" target="_blank">ƷþƷ</a>| <a href="http://www.hbjboke.cn" target="_blank">ۺϾþۺ</a>| <a href="http://www.my163.net.cn" target="_blank">ɫۺϾþþþۺһ</a>| <a href="http://www.dfyxw.cn" target="_blank">պƷרþþ</a>| <a href="http://www.ahyjj.cn" target="_blank">Ʒ۲ӰԺþ</a>| <a href="http://www.ilovegou.cn" target="_blank">Ʒþþþþù91</a>| <a href="http://www.hbqw.net.cn" target="_blank">97þù޾Ʒ</a>| <a href="http://www.qhcl233.cn" target="_blank">޹ƷþSM</a>| <a href="http://www.888su.cn" target="_blank">޹Ʒ۲ӰԺþ</a>| <a href="http://www.xygree.cn" target="_blank">þþƷŷƬ</a>| <a href="http://www.xinkb.cn" target="_blank">ҹƷþþþþž</a>| <a href="http://www.airyai.cn" target="_blank">þþƷ99Ӱ</a>| <a href="http://www.epfb.cn" target="_blank">ƷۺϾþþþþ97</a>| <a href="http://www.carmap.com.cn" target="_blank">޾Ʒھþ</a>| <a href="http://www.cbsfq.cn" target="_blank">˾þþƷһ</a>| <a href="http://www.zfam.cn" target="_blank">þ99Ʒþ</a>| <a href="http://www.wasang.cn" target="_blank">ŷ޹Ʒþѿ</a>| <a href="http://www.ranhuman.cn" target="_blank">ɫۺϾþþþ</a>| <a href="http://www.baby-photo.com.cn" target="_blank">˾þ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>