Some basic operations in Linux Kernel

Source: Internet
Author: User
Tags prefetch
Copyleft of this document belongs to yfydz and can be freely copied and reproduced when published using GPL. It is strictly prohibited to be used for any commercial purposes.
MSN: yfydz_no1@hotmail.com
This article introduces some common data structures and operations in Linux kernel. 2. The two-way linked list (list) in the Linux Kernel connects each node through the structure struct list_head. This structure serves as a parameter in the linked list element structure: struct list_head {
Struct list_head * Next, * Prev;
}; Initialization of the linked list header. Note that if the pointer in the structure is null, It is not initialization, but initialization to itself. If it is set to null under normal circumstances, rather than to itself, the system crashes. This is an easy mistake: # define list_head_init (name) {& (name), & (name)} # define list_head (name )/
Struct list_head name = list_head_init (name) # define init_list_head (PTR) do {/
(PTR)-> next = (PTR); (PTR)-> Prev = (PTR );/
} While (0) is the most common linked list operation: insert to the linked list header:
Void list_add (struct list_head * New, struct list_head * head); insert it to the end of the linked list:
Void list_add_tail (struct list_head * New, struct list_head * head); delete a linked list node:
Void list_del (struct list_head * entry); move the node to another linked list:
Void list_move (struct list_head * List, struct list_head * head); move the node to the end of the linked list:
Void list_move_tail (struct list_head * List, struct list_head * head); checks whether the linked list is empty, returns 1 blank, 0 non-empty
Int list_empty (struct list_head * head); concatenates two linked lists:
Void list_splice (struct list_head * List, struct list_head * head); get the node pointer:
# Define list_entry (PTR, type, member )/
(Type *) (char *) (PTR)-(unsigned long) (& (type *) 0)-> member) for each node in the Referer list:
# Define list_for_each (Pos, head )/
For (Pos = (head)-> next, prefetch (POS-> next); pos! = (Head );/
Pos = pos-> next, prefetch (POS-> next:
# Define list_for_each_prev (Pos, head )/
For (Pos = (head)-> Prev, prefetch (POS-> PREV); pos! = (Head );/
Pos = pos-> Prev, prefetch (POS-> PREV) Example: lish_head (mylist); struct my_list {
Struct list_head list;
Int data;
}; Static int ini_list (void)
{
Struct my_list * P;
Int I;
For (I = 0; I <100; I ++ ){
P = kmalloc (sizeof (struct my_list), gfp_kernel );
List_add (& P-> list, & mylist );
}
} A two-way linked list with the following structure is formed in the memory: + ----------------------------------------------------------- +
|
| Mylist 99 98 0 |
| + ---- ++ --------- + |
+-> | Next | ---> | list. Next | ---> | list. Next | --->... ---> | list. Next | --- +
| ---- | --------- |
+ -- | Prev | <--- | list. Prev | <--- | list. Prev | <---... <--- | list. Prev | <-- +
| + ---- + | --------- |
| Data |
| + --------- ++ --------- + |
|
+ --------------------------------------------------------------- + You can traverse the entire linked list by knowing the linked list header. If you use list_add () to insert a new node, the next direction of the linked list header is. It is easy to delete a node from a linked list: static void del_item (struct my_list * P)
{
List_del (& P-> list, & mylist );
Kfree (P );
} The most important macro is list_entry. The macro idea is to calculate the actual address of the linked list element structure based on the address of the chain header structure list_head in the linked list element structure: # define list_entry (PTR, type, member )/
(Type *) (char *) (PTR)-(unsigned long) (& (type *) 0)-> member ))) PTR is the address of the chain header structure list_head in the linked list element structure (such as struct my_list ).
Member is the name of the chain header structure list_head parameter in the linked list element structure (such as struct my_list ).
Type is the type of the linked list element structure (such as struct my_list). The calculation principle is to obtain the address of the linked list element structure based on the address of the list_head structure minus its offset position in the linked list element structure. Example: static void print_list (void)
{
Struct list_head * cur;
Struct my_list * P; list_for_each (cur, & mylist ){
P = list_entry (cur, struct my_list, list );
Printk ("Data = % d/N", p-> data );
}
} Advantage: You can use the same data processing method to describe all two-way linked lists, without having to write various editing functions for each linked list separately. Disadvantages:
1) setting the element in the linked list header to NULL is not initialization, which is different from common habits;
2) You still need to write separate functions to delete the entire linked list, and cannot process them in a unified manner, because the offset addresses of the chain header structure list_head in all linked list element structures cannot be the same, of course, if the list_head structure of the linked list header is used as the first parameter of the linked list element structure, you can use a unified function to delete the entire linked list. 3. the hash table is applicable to the scenario where you do not need to sort the entire space element, but only need to quickly find a specific element. It is a space-for-time method and is essentially a linear table, however, a large linear table is split into multiple small linear tables. Because you only need to search for small tables, the search speed increases by a lot. Ideally, the search speed of a small linear table is increased by several times. Generally, the header of a small linear table is combined into an array, and the size is the number of hash tables. The key to the hash table speed is the design of the hash function. the hash function is calculated based on fixed parameters in each element to calculate an index value not greater than the number of hash tables, indicates that the element needs to be placed in the table corresponding to the index number. For fixed parameters, the calculation result is always fixed, but for different parameter values, we hope that the calculated results can reach every index value as much as possible. The more average the hash function calculates, the more elements there will be in each small table. In this way, the better the search performance will be. The hash function should be as simple as possible to reduce the computing time. The common algorithm is to accumulate the parameter modulo in include/Linux/jhash. h has defined some hash computing functions, which can be used directly. Hash Tables are used in routing cache tables and status connection tables. For example, hash: // net/IPv4/Netfilter/ip_conntrack_core.cu_int32_t is calculated based on the tuple value in the connection trace.
Hash_conntrack (const struct ip_conntrack_tuple * tuple)
{
# If 0
Dump_tuple (tuple );
# Endif
Return (jhash_3words (tuple-> SRC. IP,
(Tuple-> DST. IP ^ tuple-> DST. protonum ),
(Tuple-> SRC. U. All | (tuple-> DST. U. All <16 )),
Ip_conntrack_hash_rnd) % ip_conntrack_htable_size );
} // Include/Linux/jhash. h
Static inline u32 jhash_3words (u32 A, u32 B, u32 C, u32 initval)
{
A + = jhash_golden_ratio;
B + = jhash_golden_ratio;
C + = initval; _ jhash_mix (A, B, C); Return C;
} 4. The timer (timer) Linux kernel timer is described by the following structure:/* include/Linux/Timer. H */
Struct timer_list {
Struct list_head list;
Unsigned long expires;
Unsigned long data;
Void (* function) (unsigned long );
}; List: timer linked list
Expires: expiration time
Function: The expiration function called when the expiration time is reached.
Data: The data passed to the expiration function. In practice, a pointer is usually converted. The Pointer Points to an operation of the timer structure: Add timer and mount timer to the system's timer linked list:
Extern void add_timer (struct timer_list * timer); Delete timer and remove timer from the system timer linked list:
Extern int del_timer (struct timer_list * timer );
(The del_timer () function may fail because the timer is no longer in the system timer linked list, that is, it has been deleted.) For SMP systems, it is best to use the following function to delete timer to prevent conflicts:
Extern int del_timer_sync (struct timer_list * timer); Modify timer and modify the expiration time of Timer:
Int mod_timer (struct timer_list * timer, unsigned long expires); common usage:
Struct timer_list is usually used as a parameter in the data structure. When initializing the structure, timer is initialized, which indicates the operation to be performed at expiration to implement the scheduled action. Generally, it is processed as timeout, the timer function is used as the resource release function for timeout. Note: If the timeout function times out, the system is in the bottom half of the clock interruption, and cannot perform complicated operations. If you want to complete some complex operations, if data is sent after expiration, it cannot be processed directly in the expiration function. Instead, it should send a signal in the expiration function to the specific kernel thread to top half for processing. To determine the order of time, the kernel defines the following macro for determination: # define time_after (A, B) (long) (B)-(long) (a) <0)
# Define time_before (a, B) time_after (B, A) # define time_after_eq (A, B) (long) (A)-(long) (B)> = 0)
# Define time_before_eq (a, B) time_after_eq (B, A) A technique is used here, because the time in Linux is an unsigned number, here, we can convert it into a signed number and then judge it to solve the time rewinding problem. Of course, it is only one round robin, but it cannot be determined twice, you can experiment on your own. 5. the kernel_thread function can be used to establish new threads in the kernel. This function is available in kernel/fork. defined in C: Long kernel_thread (INT (* fN) (void *), void * Arg, unsigned long flags) FN: main function of the kernel thread;
Arg: the parameter of the main function of the thread;
Flags: indicates the thread to be created. kernel thread functions usually call daemonize () for subsequent operations as an independent thread, and then set some thread parameters, such as names and signal processing, this is not necessary, and then enters an endless loop. This is the main part of the thread. This loop cannot be running all the time; otherwise, the system will die here, or it may be event-driven, before an event arrives, it is sleep. After the event arrives, it is wakened and operated. After the operation is completed, it continues to sleep. Alternatively, it is timed to sleep after the operation is completed, or it is added to the waiting queue to pass schedule () schedule to get the execution time. In short, the CPU cannot be occupied all the time. The following is an instance of the kernel thread, taken from kernel/context. C: int start_context_thread (void)
{
Static struct completion startup _ initdata = completion_initializer (startup); kernel_thread (context_thread, & startup, clone_fs | clone_files );
Wait_for_completion (& startup );
Return 0;
} Static int context_thread (void * startup)
{
Struct task_struct * curtask = current;
Declare_waitqueue (wait, curtask );
Struct k_sigaction SA; daemonize ();
Strcpy (curtask-> comm, "keventd ");
Keventd_running = 1;
Keventd_task = curtask; spin_lock_irq (& curtask-> sigmask_lock );
Siginitsetinv (& curtask-> blocked, sigmask (sigchld ));
Recalc_sigpending (curtask );
Spin_unlock_irq (& curtask-> sigmask_lock); Complete (struct completion *) Startup);/* install a handler so sigcld is delivered */
SA. SA. sa_handler = sig_ign;
SA. SA. sa_flags = 0;
Siginitset (& SA. SA. sa_mask, sigmask (sigchld ));
Do_sigaction (sigchld, & SA, (struct k_sigaction *) 0 );/*
* If one of the functions on a task queue re-adds itself
* To the task queue we call schedule () in State task_running
*/
For (;;){
Set_task_state (curtask, task_interruptible );
Add_wait_queue (& context_task_wq, & wait );
If (tq_active (tq_context ))
Set_task_state (curtask, task_running );
Schedule ();
Remove_wait_queue (& context_task_wq, & wait );
Run_task_queue (& tq_context );
Wake_up (& context_task_done );
If (signal_pending (curtask )){
While (waitpid (-1, (unsigned int *) 0, _ wall | wnohang)> 0)
;
Spin_lock_irq (& curtask-> sigmask_lock );
Flush_signals (curtask );
Recalc_sigpending (curtask );
Spin_unlock_irq (& curtask-> sigmask_lock );
}
}
} 6. the structure address in C is the same as the address of the first element in the structure. Therefore, the address of the first element in the structure is often used to represent the structure address in Linux kernel, pay attention to this when reading the code, which is the same as the meaning of the list_entry macro. For example:
Struct my_struct {
Int;
Int B;
} C; If (& C ==& c. a) {// always true
...
}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.