Linux thread 1

Source: Internet
Author: User

1. Linux thread implementation
A thread is further abstracted based on a process. That is to say, a process is divided into two parts: a thread set and a resource set. A thread is a dynamic object in a process. It should be a group of independent Instruction Streams. All threads in the process share resources in the process. However, threads should have their own private objects, such as program counters, stacks, and register context.
There are three types of threads:
(1). kernel thread:
Its creation and revocation are determined by the internal requirements of the kernel and are used to execute a specified function. A kernel thread does not need to be associated with a user process. It shares the global data of the kernel's body segment and has its own kernel stack. It can be separately scheduled and run on a single processor by using a standard kernel synchronization mechanism. Because kernel Thread Scheduling does not require state conversion and address space re- ing, context switching between kernel threads is much faster than Context switching between processes.
(2). Lightweight Process:
A lightweight process is a user thread supported by the kernel. It provides multi-threaded control in a single process. These lightweight processes are independently scheduled and can run on multiple processors. Each lightweight process is bound to a kernel thread and is valid in its lifecycle. Lightweight processes are independently scheduled and share the address space and other resources in the process. However, each LWP should have its own program counters, Register sets, core stacks, and user stacks.
(3). User thread:
The user thread is implemented through the thread library. They can be created, released, and managed without kernel involvement. The thread Library provides synchronization and scheduling methods. In this way, the process can use a large number of threads without consuming kernel resources, and saves a lot of system overhead. The implementation of user threads is possible, because the context of user threads can be saved and restored without kernel intervention. Each user thread can have its own user stack, a memory zone that stores user-level register context and status information such as signal shielding. The library saves the stack and register content of the current thread and loads the content of the new scheduling thread to implement scheduling and context switching between user threads.
The kernel is still responsible for process switching because only the kernel has the right to modify memory management registers. User threads are not real scheduling entities, and the kernel knows nothing about them. Instead, they only schedule processes or lightweight processes under the user thread. These processes then schedule their threads through the thread library function. When a process is preemptible, all its user threads are preemptible. When a user thread is blocked, it will block the following lightweight process. If the process has only one Lightweight Process, all of its user threads will be blocked.
Linux threads are implemented through processes. Linux kernel provides a clone () system call for Process Creation. clone parameters include CLONE_VM, CLONE_FILES, and CLONE_SIGHAND. Through clone () parameters, the newly created process, also known as LWP (Lightweight process) shares memory space with the parent process, file handle, signal processing, etc, so as to achieve the same purpose of creating a thread.
Before Linux 2.6, Linux kernel did not actually support thread. Some thread libraries were encapsulated based on user space on clone, therefore, there are some problems in signal processing, process scheduling (each process requires an additional scheduling thread), and synchronization and sharing of resources among multiple threads. In Linux 2.6, the Thread Library is called NPTL (Native POSIX Thread Library ). POSIX thread (pthread) is a programming specification. multi-threaded programs developed using this specification have good cross-platform features. Although it is implemented based on processes, the efficiency of creating threads in the new NPTL version is very high. Some tests show that it takes only 2 seconds to create 0.1 million threads Based on the NPTL kernel, and 15 minutes to create a kernel without NPTL support.
In Linux, each thread has a task_struct. Threads and processes can use the same scheduling method. From the kernel perspective, there is no difference between LWP and Process. Some are just resource sharing. If the exclusive resource is HWP, the shared resource is LWP. In the real kernel implementation

NPTL is implemented by adding futex (fast userspace mutex) in the kernel to support sleep and wake between threads. Futex is an efficient algorithm for mutually exclusive access to shared resources. Kernel plays an arbitration role in it, but it is usually completed by the process itself. NPTL is a 1 × 1 thread model, that is, a thread schedules processes in an operating system. It has the advantage of being very simple. Some other operating systems, such as Solaris, are MxN, M corresponds to the number of created threads, and N corresponds to the entities that the operating system can run. (N <M), the advantage is that thread switching is fast, but the implementation is slightly complicated.
 
2. Linux thread library pthread
(1). Test macros
# Ifdef _ POSIX_THREADS
Or sysconf (_ SC _THREADS)
To check whether macros are supported.
(2). Thread ID pthread_t
Pit_t is the only one in the system. The thread pthread_t (struct) is effective only in the process environment to which it belongs.
Determine whether two thread IDs are equal:
# Include <pthread. h>
Int pthread_equal (pthread_t tid1, pthread_t tid2 );
// If the return value is equal, a non-0 value is returned. If the return value is not equal, 0 is returned.
Obtain the pthread_t of the thread itself
Pthread_t pthread_self (void)
(3). Thread Creation
When a program is started, it is started by a single control process of a single process. To add a new thread, you can call pthread_create to create it.
# Include <pthread. h>
Int pthread_create (
Pthread_t * restrict tidp,/* The first parameter is the pointer to the thread identifier */
Const pthread_attr_t * restrict attr,/* second parameter used to set thread attributes */
Void * (* rtn) (void),/* The third parameter is the starting address of the thread running function */
Void * restrict arg/* The last parameter is the parameter for running the function */
);
// If successful, 0 is returned; otherwise, an error number is returned. It does not set errno
The New Keyword restrict in C99 and the restricted pointer (restricted pointers ). As a way for the compiler to optimize code, restricted pointers are implemented by the compiler vendor.
Restrict can only be used on pointers. If a pointer is modified by restrict) there is a special relationship between it and the object it points to-you can only use this pointer or the expression of this pointer to access the value of this object. A pointer points to a memory address. The same memory can be accessed by multiple pointers and modified when the program is running ). Restrict tells the compiler that if a memory directed by a restrict pointer is modified, no other pointer can access this memory. The compiler may select a method to optimize the part of the Code that calls the Pointer Modified by restrict, which may cause an error. Programmers have the responsibility to ensure that the Pointer Modified by restrict is correctly used as they imagined, otherwise unexpected results may occur.
If a specific memory area is not modified, it can be referred to (or referenced or accessed) by multiple restrict pointers (pointers modified by restrict ). In addition, the value assignment of the restrict pointer is limited, which is no different between function call and nested block. In a block containing a restrict pointer, only the value of the outer restrict pointer can be assigned to the restrict pointer of the inner layer. values cannot be assigned to each other in the same layer, of course, the value of the lower layer cannot be assigned to the outer layer. One exception is that when the code that declares the restrict pointer is executed, the memory referred to by this restrict pointer can be accessed by other pointers (because that restrict is the local variable of the code block, after the code block is executed, the pointer will no longer exist ).
(4). Thread termination
If any thread in the process calls exit, _ EXIT tiger, or _ exit, the whole process will be terminated. Similar to this, if the default signal action is to terminate the process, the whole process is terminated when the signal is sent to the thread.
Thread exit method:
(1). The thread only returns from the startup function. The returned value is the exit code of the thread.
(2) The thread can be canceled by other threads in the same process.
(3). The thread calls pthread_exit ().
 
Void pthread_exit (void * rval_ptr );
Rval_ptr is a non-type pointer, similar to a single parameter passed to the startup function. Other processes in the process can call the pthread_join function to access this pointer. If the thread returns from the startup function, rval_ptr contains the return code. If the thread is canceled, the memory unit specified by rval_ptr is set to PTHREAD_CANCELED. If you are not interested in the return value of the thread, you can set rval_ptr to NULL. In the example, calling the pthread_join function waits for the specified thread to terminate, but does not obtain the thread termination status. For example, in the thread startup function, return: return (void *) 1). In this way, the pthread_join function can get the exit status of the thread through the rval_ptr pointer. If the thread calls ptread_exit (void * 2), pthread_join can still get the exit state. 2. This pointer can be a complex structure type to save more information.
 
Int pthread_join (pthread_t thread, void ** rval_ptr );
/* 0 is returned for success. Otherwise, an error number is returned */
The thread that calls pthread_join will be blocked until the specified thread calls pthread_exit, starting from or canceling the function. In addition, void * rval_ptr can be directly returned as an int. For example, return (void *) 1; or pthread_exit (void *) 1); then the pointer obtained from pthread_join can be directly output using (int) ptr.
The memory address pointed to by this void * rval_ptr should not be on the stack, and its content will be overwritten by the function called below. It should be on the stack (created through malloc) or in the static data segment (using static and global variables ).
Int pthread_cancel (pthread_t tid );
/* 0 is returned for success. Otherwise, an error number is returned */
The behavior is equal to the parameter value equivalent to the pthread_exit function of PTHREAD_CANCELED.
Note: The thread can choose to ignore or control the cancellation method. Note that pthread_cancel does not wait for the thread to terminate, but simply initiates a request.
The function that the thread can schedule to execute when exiting. This is similar to the function to be executed when the atexit function of the process is scheduled to exit. Such a function is called a thread cleanup handler. A thread can create multiple thread processing functions and store them in the stack. The execution sequence is the opposite to the one they registered.
Void pthread_cleanup_push (void (* rtn) (void *), void * arg );
Void pthread_cleanup_pop (int execute );
Pthread_cleanup_pop: Delete the last registered thread cleanup handler. If execute is not 0, execute this program.
An example of how to use parameters:
 
Void
Cleanup (void * arg)
{
Printf ("cleanup: % s \ n", (char *) arg );
}
 
Void
Thr_fn1 (void * arg)
{
/*...*/
Pthread_cleanup_push (cleanup, "first cleanup in Thread ");
Pthread_cleanup_push (cleanup, "second cleanup in Thread ");
 
/*...*/
Pthread_cleanup_pop (0 );
Pthread_cleanup_pop (0 );
Return (void *) 1);/* If the end thread is returned, the cleanup handler will not call */
/* Or pthread_exit (void *) 2); pthread_exit will call normally */
}
 
Int
Main (void)
{
/*...*/
Err = pthread_create (& tid, NULL, thr_fn1, (void *) 1 );
If (err! = 0)
/* Print err and quit */
/*...*/
Err = pthread_join (tid1, & tret );
 
If (err! = 0)
/* Print err and quit */
/*...*/
 
Return 0;
}

Related Article

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.