This article mainly explains pjlib about threading implementation.
Reprint Please specify source: http://blog.csdn.net/lhl_blog/article/details/44063229
System environment:
1. Ubuntu14.04 TLS kernel 3.13.0-45-generi
2. GCC 4.8.2
3. GLIBC 2.19
Two concepts need to be explained before you begin:
1. Thread stack reference Nono's CSDN blog http://blog.csdn.net/dog250/article/details/7704898, which provides a more detailed description of the Linux process and the line stacks. Personally think that the more important thing is
The line stacks implemented by Linux glibc is not dynamically growing, and it is worth noting that if the thread definition uses a larger data structure, the thread stack overflows, and the line stacks is divided in the heap of the process.
Sputter, implicating, so that once the thread stack overflows, the process address space is indirectly destroyed, causing a bug that is difficult to debug (which could lead to memory segment errors). Thus, an effective management thread
Stack is a more important problem in multi-threaded programming. Perhaps, some people will say, I am in the multithreaded programming, for the line stacks also did not have too much intervention ah, also did not appear any problem ah? This understanding
In fact, there is a problem, the so-called debt owed sooner or later is to pay back, is not the time.
As for the apparent absence of problems, the main reasons are as follows:
1.*nix system when creating a thread, without specifying the size of the line stacks, the system creates a stack of default size (8M), and we generally do not use such ' large ' automatic variables, but if the thread defines a large number of
An array of automatic variables, for example, a char array[8*1024*1024] is defined, and no unexpected program will have a segment error, the sample code is as follows:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* ThreadFunc (void* args)
{
char c = ' 1 ';
printf ("c =%c\n", c);
Char *str1 = (char *) malloc (8*1024*1024);
Char *str2 = (char *) malloc (8*1024*1024);
Char array[8*1024*1024-1024*10] = {0};
}
int main (void)
{
pthread_t pid;
size_t Stack_size;
pthread_attr_t attr;
int rc = pthread_create (&pid, NULL, THREADFUNC, NULL);
if (rc! = 0) {
printf ("Create thread failed\n");
}
Pthread_attr_init (&ATTR);
Pthread_attr_getstacksize (&attr, &stack_size);
Stack_size/= 1024 * 1024;
printf ("Default Thread stacksize =%dmb\n", stack_size);
Pthread_join (PID, NULL);
return 0;
}
2. Thread TLS thread Locak store, thread-private data is a mechanism for storing and querying data related to a thread. The reason that this data is called thread-private data or thread-specific data is that every
A thread can independently access a copy of the data without worrying about synchronous access to other threads. Typical applications have their own errno values for each thread, and the errno values between the threads do not affect each other. It's like
Identity card definition space is like the process space of the system, each individual person is like a separate thread, each thread has a social security number, this ID number is our TLS, and our ID number
will not affect each other, of course, this analogy is somewhat inappropriate, in theory, the identity card number is static.
3.pjlib thread implementation 3.1 secure thread stack protection struct pj_thread_t
{
Char Obj_name[pj_max_obj_name];
pthread_t thread;//linux Thread ID
Pj_thread_proc *proc;//Thread Handling functions
void *arg;//thread-handling function parameters
pj_uint32_t signature1;//label 1, specific role unknown
pj_uint32_t signature2;//Label 2
pj_mutex_t *suspended_mutex;//simulated thread suspend action
If thread stack-related check processing is enabled, define the following member variable:
#if defined (pj_os_has_check_stack) && pj_os_has_check_stack!=0
pj_uint32_t stk_size;//thread Stack size J
pj_uint32_t stk_max_usage;//already using the thread stack size
Char *stk_start;//line stacks start address
The const char *caller_file;//is used to record the current position of the calling thread stack detection function
int caller_line;
#endif
};
The Pjlib thread descriptor explicitly defines the thread stack-related elements through which the pjlib thread can basically implement a secure thread stack usage. The following describes how the Pjlib line stacks is implemented.
3.1.1 Thread Stack Initialization The Pjlib Pj_thread_create implements Pjlib thread creation, where the stack_size of the function is the thread stack size parameter, and the following code retains only the parameters of the Pj_thread_create function and thread stack-related content:
/*
* Pj_thread_create (...)
*/
Pj_def (pj_status_t) pj_thread_create (pj_pool_t *pool,
const Char *thread_name,
Pj_thread_proc *proc,
void *arg,
pj_size_t Stack_size,
Unsigned flags,
pj_thread_t **ptr_thread)
{
...
#if pj_has_threads
Pj_check_stack ();
/* Set Default stack size */
if (stack_size = = 0)
Stack_size = pj_thread_default_stack_size;
#if defined (pj_os_has_check_stack) && pj_os_has_check_stack!=0
Rec->stk_size = stack_size;
rec->stk_max_usage = 0;
#endif
The first way:
#if defined (pj_thread_set_stack_size) && pj_thread_set_stack_size!=0
/* Set thread ' s stack size */
rc = Pthread_attr_setstacksize (&thread_attr, stack_size);
if (rc! = 0)
return Pj_return_os_error (RC);
#endif /* Pj_thread_set_stack_size */
The second way:
#if defined (pj_thread_allocate_stack) && pj_thread_allocate_stack!=0
/* Allocate memory for the stack */
STACK_ADDR = Pj_pool_alloc (pool, stack_size);
Pj_assert_return (STACK_ADDR, Pj_enomem);
rc = Pthread_attr_setstackaddr (&thread_attr, stack_addr);
if (rc! = 0)
return Pj_return_os_error (RC);
#endif /* Pj_thread_allocate_stack */
...
/* Create the thread. */
Rec->proc = proc;
Rec->arg = arg;
rc = Pthread_create (&rec->thread, &thread_attr, &thread_main, rec);
if (rc! = 0) {
return Pj_return_os_error (RC);
}
...
}
static void *thread_main (void *param)
{
pj_thread_t *rec = (pj_thread_t*) param;
void *result;
pj_status_t RC;
#if defined (pj_os_has_check_stack) && pj_os_has_check_stack!=0
Rec->stk_start = (char*) &rec;
#endif
....
}
Pjlib supports two types of line stacks creation, 1. Directly created using the system default thread stack size. 2. Dynamically request heap memory, and then use the heap memory address as the first address of the line stacks. After the line stacks is created, pjlib
The start address of the line stacks is initialized in the Thread_main.
3.1.2 Thread stack protection mechanism Pjlib threads implement a protection mechanism for line stacks by invoking Pj_check_stack () macros in each thread function, and the thread function performs execution first by monitoring the current line pj_check_stack state by stacks () if
The usage of the line stacks space to reach the specified police value will immediately trigger the Assert assertion prompt thread stack overflow! and exit. Conversely, Pj_check_stack () will re-update the usage of the current line stacks.
Polygons are Pj_check_stack () (Pj_thread_check_stack) implementations:
/*
* Pj_thread_check_stack ()
* Implementation for Pj_check_stack ()
*/
Pj_def (void) pj_thread_check_stack (const char *file, int line)
{
Char stk_ptr;
pj_uint32_t usage;
pj_thread_t *thread = Pj_thread_this ();
/* Calculate the current line stacks usage rate */
Usage = (&stk_ptr > Thread->stk_start)? &stk_ptr-thread->stk_start:
Thread->stk_start-&stk_ptr;
/* If the line stacks usage exceeds the police value, trigger the assertion immediately */
Pj_assert ("STACK overflow!!" && (usage <= thread->stk_size-128));
/* Re-count thread stack usage */
if (Usage > thread->stk_max_usage) {
Thread->stk_max_usage = usage;
Thread->caller_file = file;
Thread->caller_line = line;
}
}
Pj_check_stack () General thread functions are called when a local variable is defined so that the STACK overflow can be pre-alerted, reducing the amount of debugging time.
The 3.2 pjlib TLS pjlib thread implements the storage of the local thread descriptor based on the TLS mechanism, and all pjlib threads or external threads (Linux native threads) must use Pj_thread_register to store their own descriptors in TLS.
When calling the Pjlib library function, the library function generally detects whether the local thread has been registered, that is, whether the pthread_setspecific () registered thread TLS is called, or the Pjlib library function cannot be used normally, only
In order to use the Pjlib function in a fair way.
Pjlib may do so for the following reasons:
1. Each pjlib thread has a thread descriptor of pj_thread_t, and the contents of each descriptor are different, and we can use thread TLS to manage the descriptors of various threads in a uniform way, like errno.
2. Convenient thread debugging, through the TLS can easily get all the information related to the local thread, we can easily track the current calling function by printing or online debugging the thread state.
3 .....
3.3 pjlib thread scheduling policy pjlib thread encapsulates several thread scheduling policies provided by the Linux system, Sched_fifo,sched_rr,sched_other, without special settings.
A brief analysis of Pjlib thread implementation