------ Source code analysis-main program entry point (2) ------ Open Source Software Tor
----------------------------------------------------------
The second part only examines the five function call sequences following configure_backtrace_handler ().
The logic actually involves many concepts related to UNIX-like systems. Therefore, it may take a long time to explain them. First, from the perspective of the Self-Annotated function names,
It is nothing more than updating the estimated system time, tor thread and compression function initialization, log system initialization, and initialization of the monotonous timer subsystem (monotime_init ):
Call the UNIX library function time () to input the returned current time (a time_t structure instance is generally defined in the/user/include/time. h header file of a UNIX-like file system ).
Update_approx_time () (\ tor-0.3.1.8 \ src \ common \ util. c), the latter uses it to initialize the global (static) cached_approx_time variable, representing the estimated value of the current cache time. The code snippet is as follows:
It is worth mentioning that the "common" path in the tor source code tree contains some public facilities that all components will use, such as pthread, a UNIX-like system mechanism.
Encapsulation routine, encapsulation routine for the OpenSSL library, encapsulation routine for the Open Source Event Notification library libevent .... And so on.
Since update_approx_time () is called once per second, cached_approx_time is updated every second. After that, you can use approx_time () to query the last update_approx_time () call
The updated cached_approx_time.
Tor uses the above logic to implement its own timing system, avoiding the direct call of the library function time () in the Key Path. This is a kind of hack )!
In addition to the preceding Win32 platform-related code and registration crash callback, update_approx_time () is called before any tor component is initialized, reflecting the importance of time reference.
Bytes ----------------------------------------------------------------------------------------------
Tor_threads_init () (\ tor-0.3.1.8 \ src \ common \ compat_pthreads.c) sets up the public structure used by the thread, and then calls
Set_main_thread ()-> tor_get_thread_id ()-> pthread_self () mark the current thread as the main thread (stored in the global variable main_thread_id ).
In fact, all multithreading support functions in compat_pthreads.c depend on the pthread mechanism on UNIX/Linux platforms.
The purpose of this series of blog posts is to examine the theory and architecture of tor,So I don't want to try to analyze it in the underlying code of the system library function., The related code snippets are as follows:
Threads_initialized in is a global variable, which playsStatus markFrom the perspective of tor source code, such programming skills can be seen everywhere --
These statuses are initially set to 0, and some related routines (with init in the name) will detect these tags to determine whether initialization tasks have been performed, such
If threads_initialized = 0, call the pthread series functions to initialize some common facilities (attr_recursive and attr_detached) used by the thread. They are all global structure instances,
The related code snippets are as follows:
The comments in the source code have been clearly written: The pthread_attr_t struct represents a pthread attribute that separates threads;
The struct pthread_mutexattr_t representsMutex lockAttribute, which is specified as a "recursive" property-you can lock the lock again when it is held.
As mentioned above, when it comes to platform-related library functions, I usually ignore the analysis and focus on the logic of tor programs.
Speaking of the call logic of set_main_thread ()-> tor_get_thread_id ()-> pthread_self () at the end of tor_threads_init (), the related code snippets are as follows:
You can see that tor_get_thread_id () internally defines a union (union) with an "id" field,It is finally returned and assignedGlobal variable main_thread_id.
Another important focus is the tor_assert macro (\ tor-0.3.1.8 \ src \ common \ util_bug.h ),It takes corresponding measures based on the result of the expression evaluation, The related code snippets are as follows:
As mentioned in the comment, tor_assert will send an error message to the log, stderr, and then call the system service abort () to terminate the program when the assertions fail,
Therefore, the use of tor_assert must be related to the checking logic of whether the tor can run normally!
------------------------------------------------------------------
\ Tor-0.3.1.8 \ src \ common \ compress. h defines some data compression methods and compression levels ).
OnlyGzipAndZlibIt is explicitly supported by tor_compress () (compression) and tor_uncompress () (decompression). The related code snippets are as follows:
Tor_compress_init () is located in the compress. c file in the same path. It first calls atomic_counter_init () to initialize a global variable.
Total_compress_allocation(An instance of the atomic_counter_t structure) describes the total byte overhead allocated for the compression status;
Atomic_counter_init () executes memset () to initialize the content of the entire structure to 0, and then calls tor_mutex_init_nonrecursive () to process the mutex field of the structure.
Tor_compress_init () also initializes all compression modules, including zlib and uncommon lzma and zstd compression methods. The actual initialization logic is only the byte counters that are built and cleared to allocate the status of each module.
(These are all atomic_counter_t objects). The related code snippets are as follows:
Before analyzing the internal logic of atomic_counter_init (), it is necessary to discuss the atomic_counter_t structure first created by tor.
(\ Tor-0.3.1.8 \ src \ common \ compat_threads.h) -- hereinafter referred to as"Atomic counter"! The related code snippets are as follows:
It can be seen that atomic_counter_t isCompoundStructure, which contains the tor_mutex_t structure, while the latter is for the critical section under the Win32 platform.Encapsulation;
On UNIX platforms that support pthread, pthread_mutex_t is structuredEncapsulation. As follows, this design concept compatible with various platforms is indeed worth learning:
The native support for Windows-specific CRITICAL sections is achieved through the following Conditional compilation blocks:
As you can see, on the Windows platform, the tor_mutex _ * series of functions all encapsulate Windows-specific critical section related APIs --
Tor_mutex_init_nonrecursive ()->InitializeCriticalSection ()
Tor_mutex_uninit ()->DeleteCriticalSection ()
Tor_mutex_acquire ()->EnterCriticalSection ()
Tor_mutex_release ()->LeaveCriticalSection ()
The "mutex" member in the atomic counter structure encapsulates the mutex lock at the bottom of the system to protect synchronous/mutex access to the parent structure (that is, atomic_counter_t;
The object to be protected is the "val" member in it, which implements the counter function, such as tor_compress_init () this field is used to record the total byte overhead allocated for the compression status;
In fact, tor provides dedicated routines to manipulate atomic counters. In addition, it is recommended that programmers use such routines to access atomic counters through source code annotations, rather than directly modifying the data structure, avoid Unexpected error operations!
This reflects the "Object method" thinking in object-oriented programming. The related code snippets are as follows:
As you can see, before accessing the "val" field (whether added or deleted), you must first obtain the mutex lock and release it after modification. Note that all such support routines receive an atomic counter pointer,
Therefore, when calling them, you need to input an atomic counter structure address, as in tor_compress_init:
Atomic_counter_init (& total_compress_allocation );
Similarly, operations like & counter-> mutex reference the mutex member through the form parameter counter (atomic counter pointer) and then obtain its address.
Match with their parameter types (select structure members through the pointer> priority, higher than the priority of the get address operator &). The related code snippets are as follows:
We can see that the parameter type of tor_mutex_init_nonrecursive () is tor_mutex_t pointer, so atomic_counter_init () calls it
The & counter-> mutex,Again, the equivalence between pointers and addresses.
In addition, we have learned that pthread_mutex_init () should be used to initialize the mutex lock in the atomic counter .....
The internal process of tor_compress_init () can be summarized roughly as follows:
------------------------------------------------------
The third part analyzes the two remaining function calls-init_logging () and monotime_init ().