differences and connections between processes and threads
In many classic operating system textbooks, the process is always defined as an execution instance of a program, it does nothing, it only maintains the various resources required by the application, while the thread is the real execution entity.
The process must contain at least one thread in order for the process to do some work.
Process, intuitive Point said, saved on the hard disk after the program run, will be in the memory space to form a separate memory body, the memory body has its own address space, has its own heap, the superior unit is the operating system. The operating system allocates system resources in the process, so we also say that the process is the smallest unit of resource allocation.
The thread exists and processes, which is the smallest unit of operating system scheduling execution. In the popular point, the thread is the work.
If the process is a resource steward, responsible for the resources from the owner, then the thread is the work of Labor. A housekeeper must complete a job, requiring at least one laborer, that is, a process contains at least one thread, or it can contain multiple threads. Labor to work, you need to rely on the housekeeper, so say a thread, must belong to a certain process. The process has its own address space, the thread uses the process's address space, that is, the resources in the process, the threads are authorized to access, such as heap ah, stack ah, static storage area and so on.
The thread is a proletariat, but the proletariat work, must have their own labor tools, this labor tool is the stack, the thread has its own stack, the stack is still using the process of the address space, but this space is marked as a thread for the stack. Each thread will have its own private stack, which cannot be accessed by other threads.
Process is maintained by the program contains resources (static resources), such as: Address space, open file handle set, file system status, signal processing handler, and so on;
The thread maintains the running related resources (dynamic resources), such as: run stack, scheduling related control information, signal set to be processed, etc.;
However, the Linux kernel has not been the thread concept for a long time. Each executing entity is a task_struct structure, often called a process.
A process is an execution unit that maintains the dynamic resources associated with execution. At the same time, it also refers to the static resources required by the program. When you create a child process from the system call clone, you can selectively let the child process share the resources referenced by the parent process, which is often referred to as a lightweight process.
Threads on Linux are based on lightweight processes and are implemented by the user-configured Pthread library. After using Pthread, in the view of the user, each task_struct corresponds to a thread, and a set of threads and a set of resources that they collectively refer to is a process.
However, a set of threads is sufficient not just to reference the same set of resources, they must also be considered as a whole.
In this respect, the POSIX standard presents the following requirements:
1) When viewing a list of processes, a related set of task_struct should be presented as a node in the list;
2) The signal sent to this "process" (corresponding to the kill system call) will be shared by the corresponding set of task_struct and processed by any of the "threads";
3) A signal sent to a "thread" (corresponding to Pthread_kill), will be received only by a corresponding task_struct, and processed by itself;
4) When the "process" is stopped or resumed (corresponding to the sigstop/sigcont signal), the corresponding set of task_struct states will change;
5) When the "process" receives a fatal signal (such as a SIGSEGV signal due to a segment error), the corresponding group of task_struct will all exit;
6) etc. (may not be complete)
linuxthreads
Before Linux 2.6, the Pthread line libraries The corresponding implementation is a named Linuxthreads Lib.
Linuxthreads uses the lightweight process mentioned earlier to implement threads, but for the requirements of POSIX, linuxthreads except for the 5th ( when the "process" receives a fatal signal (such as a SIGSEGV signal due to a segment error), The corresponding set of task_struct will all exit ), none implemented (actually powerless):
1) If you run a program and a program creates 10 threads, you will see 11 a processes when executing the PS command under the shell, instead of 1 (note, not 10, as explained below);
2) Whether it is kill or pthread_kill, the signal can only be received by a corresponding thread;
3) The Sigstop/sigcont signal only works on a single thread;
Fortunately Linuxthreads achieved the 5th, I think this is the most important. If a thread is "hung", the entire process is running as if it were not working, and there may be a lot of inconsistencies. The process will not be a whole, and the thread cannot be called a thread.
Perhaps this is why linuxthreads is so far away from POSIX requirements that it can exist and has been used for several years.
However, linuxthreads to achieve this "5th", still pay a lot of price, and create a linuxthreads itself a major performance bottleneck.
The next thing to say is why a program creates 10 threads , but PS has 11 a processes. Because Linuxthreads automatically creates a management thread. The "5th" mentioned above is achieved by managing threads.
When the program starts running, there is no management thread present (because although the program has linked the Pthread library, it does not necessarily use multithreading). The first time the program calls Pthread_create, Linuxthreads discovers that the management thread does not exist and creates the management thread. This administrative thread is the son of the first thread (main thread) in the process. Then in Pthread_create, a command is sent through the pipe to the management thread to tell it to create the thread. That is, all threads are created by management threads except the main thread, and the management thread is their father .
As a result, when any one of the child threads exits, the management thread receives the SIGUSER1 signal (which is specified when the child thread is created through clone). The management thread in the corresponding Sig_handler determines whether the child thread exits normally, and if not, kills all threads and then commits suicide.
So, what about the main thread? The main thread is the father of the management threads, which does not Cheng the management line when exiting. Thus, the ID number of the parent process is checked through getppid in the main loop of the management thread, if the ID number is 1, indicating that the father has exited and hosted himself to the Init process (process 1th). At this point, the management thread will also kill all the child threads and then commit suicide.
So, what if the main thread is calling Pthread_exit active exit? According to POSIX standards, other child threads should continue to run in this case. Therefore, in Linuxthreads, the main thread call Pthread_exit will not actually exit, but will be blocked in the Pthread_exit function to wait for all child threads to exit, Pthread_exit will let the main thread exit. (During this and so on, the main thread is always asleep.) )
As can be seen, the creation and destruction of threads is accomplished by managing threads, and managing threads becomes a performance bottleneck for linuxthreads. the creation and destruction of threads requires one interprocess communication, one context switch to be executed by the management thread, and multiple requests are executed serially by the management thread .
NPTL
In the Linux 2.6,glibc there is a new Pthread line libraries--NPTL (Native POSIX threading Library).
NPTL implements all the 5-point requirements for POSIX mentioned earlier:
1) When viewing a list of processes, a related set of task_struct should be presented as a node in the list;
2) The signal sent to this "process" (corresponding to the kill system call) will be shared by the corresponding set of task_struct and processed by any of the "threads";
3) A signal sent to a "thread" (corresponding to Pthread_kill), will be received only by a corresponding task_struct, and processed by itself;
4) When the "process" is stopped or resumed (corresponding to the sigstop/sigcont signal), the corresponding set of task_struct states will change;
5) When the "process" receives a fatal signal (such as a SIGSEGV signal due to a segment error), the corresponding group of task_struct will all exit;
But, in fact, instead of NPTL implementation, it's more of a Linux kernel implementation.
In Linux 2.6, the kernel has a thread group concept, and a tgid (thread group ID) field is added to the task_struct structure. If this task is a "main thread", then its tgid equals PID, otherwise tgid equals the PID of the process (that is, the main thread of the PID).
In the clone system call, passing the Clone_thread parameter sets the Tgid of the new process to the tgid of the parent process (otherwise the tgid of the new process is set to its own PID).
There are two similar xxid in task_struct: Task->signal->pgid the PID of the beginning process of the Save Process group, and the PID of the process that task->signal->session save the session. Use these two IDs to correlate process groups and sessions.
With Tgid, the kernel or the associated shell program knows whether a tast_struct represents a process or a thread, and knows when to show them and when not to show them (as in PS, the threads don't show up). The Getpid (Get process ID) system call returns the Tgid in Tast_struct, while the PID in Tast_struct is returned by the Gettid system.
There are some problems in not showing the sub-threads when executing the PS command. For example, when the program a.out run, a thread is created. Assume that the main thread's PID is 10001, the child threads are 10002 (their tgid are all 10001). At this point, if you kill 10002, you can kill 10001 and 10002 of these two threads together, even though the PS command does not see a 10002 process at all. If you don't know the story behind the Linux thread, you'll definitely feel like you're in a supernatural event .
Let's test this supernatural event together:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <pthread.h>void *fun (void * Arg) {printf ("Thread is created!\n");p ause ();//Suspend thread}int main (void) {pthread_t tid;pthread_create (&tid, NULL, fun, NULL);p ause ();//The main thread hangs (otherwise the main thread terminates, the child threads are hung) return 0;}
This program creates a thread to hang after, and the child threads in the output "thread is created!" are also suspended. Running results such as:
We open another terminal, look at the process running in the background, found that the demo process number (PID) is 2361, we use Kill to terminate the PID 2362 process (Note that the PS does not have this process), such as:
It turns out that the demo process also terminates, such as. The reason is that 2362 is the thread number of the created thread, the thread terminates abnormally, and the corresponding process terminates.
In order to cope with the "signal sent to the process" and "send the signal to the thread", task_struct inside maintained two sets of signal_pending, one is the thread group shared, one set is thread-unique .
Signals sent through kill are placed in the signal_pending shared by the thread group and can be handled by any one of the threads, and the signal sent through Pthread_kill (Pthread_kill is the interface of the Pthread library, the corresponding system call Tkill) is placed in a signal_pending that is unique to the thread, and can only be handled by this threading.
When a thread stops/continues, or if it receives a fatal signal, the kernel applies the processing action to the entire thread group.
ngpt
Speaking of which, incidentally, Ngpt (Next Generation POSIX Threads).
both of the above-mentioned libraries use kernel-level threads (each thread corresponds to a dispatch entity in the kernel), which is called a 1:1 model (1 threads correspond to 1 kernel-level threads);
NGPT, however, intends to implement the M:N model (M threads correspond to N kernel-level threads), which means that several threads may be implemented on the same execution entity. The line libraries needs to abstract several execution entities on the execution entity provided by the kernel, and implement the scheduling between them. Such an abstract execution entity is called a user-level thread.
In general, this can be done by assigning a stack to each user-level thread, and then by longjmp the context switch. (Baidu "setjmp,longjmp", you know. )
But in fact there are so many details to deal with that the current ngpt doesn't seem to have implemented all the expected functions and is not ready to do so for the time being.
The switching of user-level threads is obviously faster than kernel-level threading, which may be a simple long jump, while the latter requires saving/loading registers, entering and exiting the kernel state. (Process switching will also need to switch the address space, etc.)
A user-level thread cannot enjoy multiprocessor because multiple user-level threads correspond to a kernel-level thread, and a kernel-level thread can run on only one processor at a time.
However, M:N's threading model provides a means to enable threads that do not require parallel execution to run on several user-level threads corresponding to a kernel-level thread, saving them from switching overhead.
It is said that some UNIX-like systems (such as Solaris) have implemented a more mature m:n threading model, which has some advantages over Linux threads.
Turn from: Linux Threading analysis
A brief analysis of Linux threads