Chapter III Process Management
3.1 Process
1, the process is in the implementation period of the procedure, the process is the real-time results of the executing program code, the process is in the execution period of the program and the associated resources collectively, the process includes code snippets and other resources.
Thread: Is an object that is active in the process.
2. The thread of execution, or thread, is the object that is active in the process. Each thread has a separate program counter, a process stack, and a set of process registers.
3. The kernel dispatches the object to be a thread, not a process. Linux does not differentiate threads in particular, it is considered a special process
4, in the modern operating system, the process provides two virtual mechanisms: Virtual processor and virtual memory. Virtual memory can be shared between threads, but each has its own virtual processor.
5, the program itself is not a process. In fact, it is entirely possible that two or more different processes are executing the same program.
6. The process begins to survive at the moment it is created. In a Linux system, this is usually the result of calling the fork () system.
7. The fork () system call returns two times from the kernel: one time back to the parent process, and another to the newly generated child process.
8. Fork () is actually implemented by the clone () system call.
9. The program exits the program through the exit () system call.
10, another name of the process is the task.
11. EXEC (): Create a new address space and load the new program into it.
WAIT4 (): Whether the parent process queries the child process for finalization
Wait (), Waitpid (): The program exits execution and becomes zombie, calling both to destroy.
3.2 process descriptors and task structures
1. The kernel stores the list of processes in a two- way circular chain table called the task queue. Each item in the list is a structure of type task_struct, called the process descriptor, which is defined in the <linux/sched.h> file and contains all the information for a specific process.
2. The data contained in the process descriptor can fully describe an executing program: The file It opens, the address space of the process, the pending signal, the status of the process, and more information.
I. Assigning process descriptors
1, Linux through the slab allocator distribution task_struct structure, this can achieve object reuse and cache coloring purposes.
2, Slab splitter-dynamically generated, simply create a new structure struct thread_info at the bottom or top of the stack.
The THREAD_INFO structure of each task is allocated at the end of its kernel stack. A task field in a structure holds a pointer to the actual task_struct of the task.
Second, the process descriptor storage
1. The kernel identifies each process by a unique process identity value PID. The PID type is pid_t, is actually an int type, the maximum value defaults to 32768, the upper limit private change/proc/sys/kernel/pid_max.
2, the PID is stored in the respective process descriptor.
3. Find the process descriptor to the currently running process through the current macro.
4. In x86, the THREAD_INFO structure is created at the end of the kernel stack, and the TASK_STRUCT structure is found indirectly by calculating the offset.
5, current through Current_thread_info () the stack pointer after the 13 significant bits are masked, and then extracted from the Thread_info task field and return the address of task_struct.
Iii. Status of the process
1. The state field in the process descriptor is used to describe the current status of the process. A total of five states, marked as follows:
(1) task_running (run): The process is executable, either executing, or waiting to be executed in the run queue.
(2) task_interruptible (interruptible): process is sleeping/blocked
(3) Task_uninterruptible (non-interruptible): The sleep/blocked process is not awakened by the signal
(4) Task_traced: Processes tracked by other processes
(5) task_stopped (STOP): The process stops executing, the process is not running and cannot be put into operation.
2, received Sigstop, SIGTSTP, Sigttin, Sigttou and other signals, or when debugging received any signal, can enter this state.
Iv. Setting the status of the current process
1, use Set_task_state (task,state) function. Sets the status of task tasks to state.
2, Set_current_state (state) and Set_task_state (current,state) are equivalent.
V. Process context
1, the program execution system call or trigger exception, will fall into the kernel space, this time the kernel "on behalf of the process Execution" and in the process context. The current macro is valid in this context.
2, process access to the kernel must pass through the interface: System calls and exception handlers.
Vi. Process Family Tree
1. All processes are descendants of the init process with PID 1.
2. The kernel starts the INIT process in the final stage of system startup.
3, each process in the system must have a parent process, can have 0 or more child processes, the process of having the same parent process is called a brother. This relationship is stored in the process descriptor, which points to the parent process Task_struct,children is the child process chain list.
4. Get the process descriptor of the parent process: struct Task_struct *my_parent = current->parent;
5. Access Child process:
struct Task_struct *task;
struct List_head *list;
List_for_each (list, ¤t->children) {
Task = list_entry (list, struct task_struct, sibling);
/* Task now points to one of the current child processes */
}
The process descriptor of the INIT process is statically allocated as Init_task.
6. Get the next process in the list: List_entry (task->tasks.next, struct task_struct, tasks);
7. Get the previous process in the list: List_entry (Task->tasks.prev, struct task_struct, tasks);
The above relies on the two macro implementations of Next_task (Task) and Prev_task (Task).
8. For_each_process (Task) macro, which accesses the entire task queue in turn, and each access task pointer points to the next element in the list.
struct Task_struct *task;
For_each_process (Task) {
/* It prints out the name and PID of each task */
PRINTK ("%s[%d]\n", Task->comm, Task->pid);
}
3.3 Process Creation
One, copy on write
1. Fork (): Creates a child process by copying the current process. The difference between a child process and a parent process is only pid,ppid and some resources and statistics.
2. EXEC (): reads the executable file and loads it into the address space to start running.
3, write-time copy is a kind of technology that can postpone or even eliminate copying data, the kernel does not copy the entire process address space, but rather let the parent process and the child process share a copy.
4. Replication of resources occurs only when a write is required, before this is read-only.
5. The actual cost of fork is to copy the page table of the parent process and create a unique process descriptor for the child process.
Second, fork ()
1. Linux implements Fork () via Clone () system call.
2, Do_fork complete the creation of a lot of work, defined in the Kernel/fork.c file. The function calls the Copy_process () function to let the process begin running.
3. The approximate steps to create the process are as follows:
(1) fork (), vfork (), and __clone () call Clone () according to their desired parameter flags.
(2) The Clone () to call Do_fork ().
(3) Do_fork () calls the Copy_process () function and then lets the process start running.
(4) returns the Do_fork () function, if the copy_process () function returns successfully, the newly created child process is awakened and put into operation. The kernel intentionally chooses the child process to execute first.
Third, vfork ()
1. The vfork () system call and fork () function are the same except for page table entries that do not copy the parent process.
2, the system is best not to call Vfork ().
3, Vfork () the implementation of the system call is done by passing a special flag to clone ().
(1) Call Copy_process () is, Task_struct's Vfor_done member is set to NULL.
(2) when executing do_fork (), if given a specific flag, Vfor_done will point to a specific address.
(3) After the child process begins execution, the parent process does not resume execution immediately, but waits until the child process sends a signal to it through the Vfor_done pointer.
(4) when calling Mm_release (), the function is used for the process to exit the memory address space and to check if Vfor_done is empty, and if not NULL, a signal is sent to the parent process.
(5) Back to Do_fork (), the parent process wakes up and returns.
3.4 The implementation of threads in Linux
1. Threading mechanism is an abstract concept commonly used in modern programming technology, which provides a set of threads running in shared memory address space within the same program, can share open files and other resources, supports concurrent programming, and can guarantee true parallel processing on multiprocessor systems.
2, from the kernel point of view and there is no thread this concept, it all threads as a process to implement, the thread is only considered a process of sharing some resources with other processes.
First, create a thread
1. Thread creation is similar to normal process creation, except that you need to pass some parameter flags when calling clone () to indicate the resource that needs to be shared: Clone (CLONE_VM | Clone_fs | Clone_files | Clone_sighand, 0);
Both the parent and the child share the address space, file system resources, file descriptors, and signal handlers.
2, the common Fork () Realization: Clone (SIGCHLD, 0);
Implementation of Vfork (): Clone (Clone_vfork | CLONE_VM | SIGCHLD, 0);
3. The parameter flags passed to clone () determine how the new creation process behaves and what kind of resources are shared between the parent and child processes.
Second, kernel thread
1. Kernel thread: A standard process that runs independently in kernel space.
2, Kernel threads do not have a separate address space, only run in the kernel space, never switch to user space, can be scheduled and preempted.
3. Kernel threads can only be created by other kernel threads. The kernel is automatically processed by deriving all new kernel threads from the Kthreadd kernel process. Declare the interface in <linux/kthread.h>.
4. The new task is created by the Kthread kernel into the system calling process through Clone ().
5. The kernel thread starts up and runs until the call Do_exit () exits, or the other part of the kernel calls Kthread_stop () exits, and the arguments passed to Kthread_stop () are returned by the Kthread_create () function task_ The address of the struct structure.
3.5 Process End
1. When a process is terminated, the kernel must release the resources it occupies and inform the parent process.
2, the reason for the end of the process: generally from itself, occurs when calling the exit () system call. This system call may be called explicitly, or it may be implicitly returned from a program's main function.
3, no matter how the process is terminated, the task is mostly done by Do_exit ().
First, delete the process descriptor
1. The task_struct structure of the child process is freed after the parent process obtains the terminated child process information and notifies the kernel that it is not concerned.
2, wait () This family function is implemented through a unique system call WAIT4 (). Its standard action is to suspend the process that invokes it until one of the child processes exits, at which point the function returns the PID of the child process.
3. When you release the process descriptor, you need to call Release_task ().
Ii. the dilemma caused by the orphan process
1, if the parent process exits before the child process, there must be a mechanism to ensure that the child process can find a new father, otherwise these orphaned processes will always be in a zombie state at the exit, wasting memory.
2. Solution:
(1) to the child process to find a thread within the current thread group as the father,
(2) If not, let Init do their parent process.
Exit_notify () is called in Do_exit (), which calls Forget_original_parent () and the latter invokes Find_new_reaper () to perform the parent-seeking process.
It then iterates through all the child processes and sets up a new parent process for them.
Then call Ptrace_exit_finish () to do the same for the new parent process, which is to find the father for Ptraced's child process.
Traversed two linked lists: the child process list and the Ptrace child process chain list, which sets a new parent process for each child process.
The final init process routinely calls wait () to check its child processes, clearing all of its associated zombie processes.
3.6 Summary
In this chapter, we learn the core concepts of the operating system-the process. Discusses how Linux stores and represents processes (with task_struct and Thread_info), how to create a process (via fork (), essentially clone ()), and how to load a new execution image into an address space (via the exec () system call family). How to represent the hierarchical relationship of a process, how the parent process collects information about its descendants (through the wait () system call family), and how the process eventually dies (forced or voluntary call exit ()). Process is a very basic, very critical abstraction, located at the heart of every modern operating system, and the ultimate reason we have an operating system (to run programs).
Resources
"Linux kernel Design and implementation" version 3rd
Linux Kernel Analysis--chapter III Process Management