The data contained in the process descriptor can fully describe an executing program: The file he opened, the address space of the process, the pending signal, the status of the process, and so on.
? 1: Assigning process descriptors
Linux allocates task_struct structures by using the slab allocator to achieve object reuse and cache coloring. Now just create a new struct struct thread_info structure at the bottom or top of the stack.
First, let's look at the structure of the Thread_info:
structThread_info {//The process structure in which it resides structTask_struct *task;/ * Main Task structure * / //execution domain structExec_domain *exec_domain;/ * Execution domain * / //Bottom sign unsigned LongFlags/* Low Level flags * / //thread synchronization flag unsigned LongStatus/ * thread-synchronous flags * / //Current CPU__U32 CPU;/ * Current CPU * / //0: Indicates preemption, less than 0:bug intPreempt_count;/ * 0 = preemptable, <0 = BUG * / /* * Process address space * 0-0XBFFFFFFF for user process * 0-0XFFFFFFFF for kernel process */mm_segment_t Addr_limit;/ * Thread address space:0-0xbfffffff for User-thead 0-0xffffffff For Kernel-thread * / void*sysenter_return;structRestart_block Restart_block;unsigned LongPREVIOUS_ESP;/* ESP of the previous stack in case of nested (IRQ) stacks */__u8 supervisor_stack[0];//Put on top of the stack};
Through this structure, we can see that the main knowledge of thread_info structure, you can obtain the required process.
The THREAD_INFO structure of each task is allocated at the end of his kernel stack.
? 2: Storage of process descriptors
The kernel identifies each process through a unique process identity value or PID. This value has an upper limit and is stored in the/proc/sys/kernel/pid_max. We can look at:
We can modify this value to change the maximum number of processes in the system.
Let's take a look at the use of the Current_thread_info () function.
Get PID of the current process we can use this:
#include <asm/thread_info.h>//....int pid = current_thread_info()->task->pid;//....
3: Process Status
Below, take a look at our Linux kernel code:
/* Task State bitmask. note! These bits is also * encodedinchFs/proc/array.c:get_task_state (). * * We have both separate sets ofFlags:task->state * is AboutRunnability, whileTask->exit_state is * About theTask exiting. Confusing, butThis by * modifying oneSetCan ' t modify theOther One by* mistake. */#define TASK_RUNNING 0#define TASK_INTERRUPTIBLE 1#define Task_uninterruptible 2#define TASK_STOPPED 4#define TASK_TRACED 8/*inchTsk->exit_state * *#define Exit_zombie#define Exit_dead/*inchTsk->state again */#define Task_noninteractive
These lines of code describe all the states of a process. Where, in the task_struct struct, the property that holds the state of the process is the status domain. Overall, the process has a total of 5 in the state, let's tidy up below.
Task_running-The process is executable. Indicates that the process is executing, or waiting to be executed in the task queue. In user space, this is the only possible state of the process, which can also be in kernel space.
Task_interruptible-The process can be interrupted. The process is sleeping, waiting for an event to be reached. Once this event is reached, the status of the process becomes operational. Of course, this state can also wake up early because of a signal.
Task_uninterruptible-The process is non-disruptive. The state does not react to the signal. He waits for a specific event to occur and the other event signal cannot wake it.
Task_traced-processes that are tracked by other processes. For example, the debugger is tracked by Ptrace.
Task_stopped-the process stops executing.
The following diagram is a transformation of the process state.
4: Set the current process state
While the kernel is running, it needs to adjust the state of a process often, and here are a few ways to set the state of the process.
?1:set_task_state(task,state); /* 将任务task的状态设置为state?2:如果没有内存屏障的话,也可以使用下面的方法来设置进程状态? ?task->state = state;
Where the function set_current_state (state) and Set_task_state (current,state) are equivalent.
Let's take a look at the definitions of some of these functions.
#define __set_task_state (tsk, State_value)do {(tsk)->state = (state_value);} while(0)#define Set_task_state (tsk, State_value)SET_MB ((tsk)->state, (State_value))//* Set_current_state () includes a barrier so that the Write ofCurrent->state * isCorrectly serialised WRT theCaller ' s subsequent test ofWhether to* Actually sleep: * * set_current_state (task_uninterruptible); *if(Do_i_need_to_sleep ()) * Schedule (); * * If theCallerdoes notNeed such serialisation ThenUse __set_current_state () */#define __SET_CURRENT_STATE (state_value)do {current->state = (state_value);} while(0)#define SET_CURRENT_STATE (state_value)SET_MB (Current->state, (State_value))
There will be a problem with SMP's memory barrier, which is not discussed here first.
In the above code, we found that in the macro definition, using a do{}while () structure, in the Linux kernel source code, there are many such structures, the following we will analyze the benefits of using this structure.
Suppose we have such an example:
#definefun(x)hello1(x);hello2(x)
If we have such a piece of code
if(x) ? ?fun(x);
In that case, after replacing it, we'll be like this:
if(x) hello1(x); hello2(x);
This will lose our original purpose.
If we use {} to enclose the macro definition. This defines:
#definefun(x) {hello1(x);hello2(x);}
If our code is like this:
if(x) fun(x);else test(x);
After we replace it, it becomes this:
if(x){ hello1(x); hello2(x);};else test(x);
This is also wrong, so we use the do{}while () structure function to be useful.
5: Process Family tree
In Linux, all processes are descendants of the init process with PID 1. The kernel initiates the INIT process at the last stage of system startup. Each process in the system has a parent process, and each process has 0 or more child processes. All processes that have the same parent process become sibling processes. Similarly, the relationships between processes are stored in the process descriptor. Each process has a pointer to a parent process and a list of the child processes.
The code in the process descriptor is as follows:
struct task_struct *parent; /* parent process */ /* thelistofmythe * tasks I‘m ptracing. */ struct list_head children; listofmy children */
1: Gets the parent process of the current process
??struct task_struct *myparent = current->parent;
2: Traversing child processes
?struct task_struct *task; ?struct list_head *list; ? ?list_for_each(list,¤t->children){ ? ?task = list_entry(list,struct task_struct,slibinig); ? ?//task指向当前的某个子进程 ?}
? 3: Gets the init process, the process descriptor of the INIT process is statically assigned as Init_task.
?struct task_struct *task; ?for(task = current;task != &init_task;tasktask->parent); ?/*指向init进程 */
4: Another way to traverse a process
?struct task_struct *task; ?for_each_process(task){ ? ?//进行操作 ?} ?
Process Management (II)