I. Process status
The state field in the process descriptor describes the current state of the process, which consists of a group of signs, each of which describes a possible state of the process, these States are mutually exclusive, that is, at the same time, only one status can be set.
1) running status: task_running. The process either runs on the CPU or is ready to run.
2) The stoppedwait status is task_interruptible. The process is suspended until it receives a message and wakes up the process.
3) Non-disruptive waiting status: task_uninterruptible. When the waiting condition is true, the status of the current process cannot be changed.
4) paused status: task_stopped. When a paused process receives a paused signal, it enters the paused status.
5) tracking status: task_traced. The execution of a process is suspended by the debugger program. When a process is monitored by another process, any signal can set this process to tracking status.
Two other States can be stored in the state field of the Process descriptor or in the exit_state field. The names of the two processes show that only when the process is terminated, only in these two statuses
6) dead state: exit_zombie. The execution of the process is terminated. However, the parent process has not released the wait4 () and waitpid () system calls to return information about the dead process. Previously, the kernel cannot discard data contained in the dead process descriptor, and the parent process may need it
7) stiff withdrawal status: exit_dead, final state. The parent process has released wait4 () and waitpid () system calls, so the system is deleted by the system, in order to prevent other execution threads from executing wait () system calls on the same process, the state of the process is changed from dead state to dead state.
The value of the state field is usually set by a simple value assignment statement:
P-> state = task_running;
The kernel also uses the set_task_state and set_current_state () macros, which respectively set the status of the specified process and the status of the current process.
Ii. Process Descriptor
3. Mark a thread
There is a very strict one-to-one correspondence between the process and the process descriptor, which makes it easy to mark the process with the 32-bit process descriptor address. The process descriptor Pointer Points to this address, the kernel uses the process descriptor pointer to reference most processes.
PID can also mark a process (in Unix-like operating systems). PID is a process identifier and is stored in the PID field of the Process descriptor. The PID is sequentially numbered, the newly created process is the PID + 1 of the previous process, and the PID value has an upper limit. When the upper limit is reached, the idle minimum PID number must be used cyclically. Because the PID Number is used cyclically, the kernel must manage a pidmap _ array bitmap to indicate the allocated PID Number and idle PID Number.
The POSIX 1003.1c Standard specifies that all threads in A multithreaded application must have the same PID. All threads of A multithreaded application share the same PID.
4. Process descriptor Processing
The thread_info data structure and the process Kernel stack are stored in two consecutive page boxes. The thread_info structure starts from the 0x015fa000 address, and the process Kernel stack starts from the ox015fc000 address, the ESP register points to the top of the current stack whose address is 0x015ff878. The kernel uses alloc_thread_info and free_thrad_info macros to allocate and release the thread_info structure and the memory zone of the kernel stack.
5. Two-way linked list in Linux Kernel
The Linux kernel defines the list_head data structure. The fields next and Prev indicate the forward and backward pointer elements of the universal two-way linked list, respectively.
List--head (list_name): Creates a new linked list.
List_add (n, p): insert the element pointed to by N to a specific element pointed to by P.
List_add_tail (n, p), insert the element N points to before the specific element P points
List_del (P): deletes the element pointed to by P.
List -- empty (p) check whether the linked list pointed to by the address P of the first element is empty
6. process linked list under task_running
In earlier Linux versions, all the processes that can run are placed in the same running Queue (linked list). Because the overhead of maintaining this linked list in priority sorting is too large, early schedulers may not scan the entire queue when selecting the best processes to run.
The implementation of linux2.6 is somewhat different. The purpose is to select the best running process within a fixed period of time, regardless of the number of processes in the queue.
The method to increase the scheduling speed is to create multiple lists of processes that can run. Each process priority corresponds to a different list. If the priority of a process is K, the process is put in the chain list with the priority of K.
The kernel must store a large amount of data for each running queue in the system. However, the main data structure of the running queue is the chain table of process descriptors of the running queue, all these linked lists have a separate prio_array _ data structure implementation. The fields are described as follows:
Number of process descriptors in the int nr_active linked list
Unsigned long [5] bitmap priority bitmap: if and only when the linked list of a priority process is not empty, set the corresponding Bit Flag
Struct list_head [15] header node of the priority queue of queue 140
VII. Relationship between processes
Processes created by a program have a parent-child relationship. If a process creates multiple processes, these sub-processes have a sibling relationship. Several fields are introduced in the Process descriptor to indicate these relationships. process 0 and 1 are created by the kernel, and process 1 is the ancestor of all processes.
Description of the field in the process descriptor of P that represents the process kinship
Real_parent: point to the process descriptor for creating P. If the parent process of P does not exist, it points to the process descriptor of process 1.
Parent: The current parent process pointing to P, which is generally consistent with real_parent.
Children: the head of the linked list. All elements of the linked list are created by P.
Sibling: pointer to the next element in the sibling process linked list or the previous element. These sibling elements are created by P.
Process descriptor field for establishing non-kinship:
Group_leader: descriptor pointer of the lead process in the process group where p is located
Signal-> pgrp: PID of the lead process in the process group where p is located
Tgid: PID of the lead process in the thread group where p is located
Signal-> session: PID of the logon session leader PROCESS OF P
Ptrace_children: the head of the linked list, which contains all the sub-processes of P tracked by the debugger program.
8. waiting queue
Waiting queue has many functions, especially for interrupt processing, process synchronization, and timing. The waiting queue implements the conditional waiting on the re-event: the process waiting for a specific event puts itself in the appropriate waiting queue and gives up control. Therefore, the waiting queue indicates a group of sleeping processes. When a certain condition is true, the kernel will wake them up.
A waiting queue is implemented by a two-way linked list. Its elements include pointers to process descriptors. Each waiting queue has a waiting queue header. The waiting queue header is a data structure of the wait_queue_head_t type.
Struct _ wait_queque_head
{
Spinlock_tlock;
Structlist_head task_list;
};
Typedefstruct _ wait_queque_headwait_queque_head_t;
Synchronization is achieved by waiting for the lock spin of the queue header, and the task_list field is the head of the waiting process linked list.
Element Types in the waiting queue linked list:
Struct _ wait_queue
{
Unsigned intflags;
Structtask_struct * task;
Wait_queue_func_t func;
Structlist_head task_list;
};
Typedefstruct _ wait_queuewait_queue_t;
Each element in the wait queue linked list represents a sleep process, which waits for an event to occur. Its descriptor is placed in the task field, and the task_list field contains pointers, this pointer connects an element to the linked list of processes waiting for the same event.
There are two types of sleep processes: mutex process (wait for the flags field of the queue element to be 1) Wake up by the kernel, rather than mutex process (the flags field is 0 ), the kernel always wakes up when an event occurs.
9. Waiting for queue operations
You can use the declare_wait_queue_head (name) macro to define a new waiting queue header. It statically declares a waiting queue header variable named name and initializes the lock and task_list fields of the variable. The init_waitqueue_head () function can be used to initialize the dynamically allocated waiting queue header variable.
The init_waitqueue_entry (p, q) function initializes the variable q of the wait_queue_t structure as follows:
Q-> flags = 0;
Q-> task = P;
Q-> func = default_wake_function;
Non-mutex process P will be awakened by default_wake_function.
Once an element is defined, it must be inserted into the waiting queue. The add_wait_queque () function inserts a non-mutex process into the first position of the waiting queue linked list, add_wait_queue_exclusive () the function inserts a mutex process into the last position of the waiting queue linked list. The remove_wait_queue () function deletes an element from the waiting queue linked list. The waitqueue_active () function checks whether a given waiting queue is empty.
A process waiting for a specific condition can call any of the following functions:
1. sleep_on () is used to operate the current process.
Void sleep_on (wait_queue_head_t * WQ)
{
Wait_queue_twait;
Init_waitqueue_entry (& wait, current );
Current-> state = task_uninterruptible;
Add_wait_queue (WQ, & wait );
Schedule ();
Remove_wait_queue (WQ, & wait );
}
2. The interruptible_sleep_on () and sleep_on () functions are the same, but slightly different. The former sets the current process status to task_interruptible, and the latter to task_uninterruptible.
3. sleep_on_timeout () and interruptible_sleep_on_timeout () Allow the caller to define a time interval. After the time interval expires, the process will be awakened by the kernel.
4, prepare_to_wait (), prepare_to_wait_exclusive (), finish_wait ()
5. The wait_event and wait_event_interruptible macros enable their calling processes to wait for the queue to sleep until the conditions are modified.
The kernel uses any macro below to wake up the processes in the waiting queue and set their status to task_running.
Wake_up, wake_up_nr, wake_up_all, wake_up_interruptible, and wake_up_locked.
When a process is awakened, all mutex processes are at the end of the two-way linked list, and all non-mutex processes are at the beginning of the linked list, therefore, the function always wakes up non-mutex processes and then wakes up the mutex process.