Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How does Glib encapsulate a cross-platform thread library

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

Glib how to encapsulate cross-platform thread library, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain for you in detail, people with this need can come to learn, I hope you can gain something.

I. Preface

This article, according to the following two diagrams, describes how glib designs the thread library on the Linux and Windows platforms.

Linux platform:

Windows platform:

Recently, I have written several articles about cross-platform application design ideas, and some partners have left messages in the background to ask about some general cross-platform libraries. It seems that there are still a lot of requirements in this area.

The so-called cross-platform is nothing more than the hope that the code of the same application can be compiled into executable programs that run on multiple platforms.

So how do you make your application code platform independent? Obviously need a bridge layer in the middle, throw the annoying code related to the platform that you don't want to deal with to this middle layer to deal with.

To put it simply: those who need to deal with the dirty work and hard work related to the platform are done by this middle tier for you. When we write applications, we only need to care about our own business layer transactions.

Without this middle tier, your code may be filled with a lot of # if...#else code.

And glib is such a middle-tier cross-platform library, it provides a lot of commonly used packaging, thread library is only one of them, this article we mainly to learn how glib is to design cross-platform thread library.

II. Brief introduction of glib

At first glance, it's easy to confuse glib with glibc, both of which are GPL-based open source software, but belong to completely different concepts.

Glibc is a standard C function library implemented by GNU, while glib is a set of function libraries of gtk+.

So what is gtk+? Friends who use Linux must know the desktop environment of gnome. Gnome is a desktop system based on gtk+, and glib is the unsung hero behind gtk.

Glib can be used on multiple platforms, such as Linux, Unix, Windows, etc. Glib provides a corresponding substitute for many standard and commonly used C language structures.

As a C language developer, sometimes we envy C++ developers very much. There are so many tools available in the standard library (SDL): linked lists, vectors, string processing.

But what about the C language? You have to realize these wheels on your own everywhere.

But on the other hand, if we accumulate the useful wheels we have written and learn from others in our daily development process to form our own "treasure house", it is also a manifestation of experience and a kind of competitiveness.

Today, there are many Lei Feng implemented high-quality C libraries on github: some focus on cross-platform, some focus on a particular area (such as: network processing, formatted text parsing).

While addressing cross-platform issues, glib also provides many other useful toolkits, such as event loops, thread pools, synchronization queues, memory management, and so on.

Since it provides many functions, it will inevitably lead to a larger volume. This is why many developers give up glib when faced with different choices.

Anyway, since glib is so powerful, we can learn its design ideas, which can improve a person's meta-skills more than blindly typing thousands of lines of code!

Third, the design of thread library

1. Thread-related files

In Linux systems, threads are generally created through the POSIX interface (portable operating system interface). For example, the API function for creating threads is pthread_create (...).

In Windows systems, there are several ways to create threads:

CreateThread ()

_ beginthread ()

Since the glib library is designed to solve cross-platform problems, it must provide a unified interface when facing application layer programs upward, and call thread functions in different systems when facing different operating systems downwards.

Glib encapsulates these thread-related operations in platform-related code, as shown in the following figure:

Linux system: gthread.c, gthread_posix.c participate in compilation, generate glib library

Windows system: gthread.c, gthread_win32.c participate in compilation, generate glib library

About this cross-platform way of building files (that is, compiling), I suggest you take a look at this short article: three ways to organize cross-platform code

two。 Data structure

You must have heard of this formula: program = data structure + algorithm. For a C language project, understanding the design of the data structure is very important for understanding the idea of the whole program, as well as in glib.

When designing the thread library, glib is divided into two levels: platform-independent part and platform-related part.

Platform-independent data structures are (some code that does not affect understanding is deleted):

Struct _ GThread {GThreadFunc func; gpointer data; gboolean joinable;}; typedef struct _ GThread GThread; struct _ GRealThread {GThread thread; gint ref_count; gchar * name;}; typedef struct _ GRealThread GRealThread

The platform-related data structures are:

Linux system:

Typedef struct {GRealThread thread; pthread_t system_thread; gboolean joined; GMutex lock; void * (* proxy) (void *); const GThreadSchedulerSettings * scheduler_settings;} GThreadPosix

Windows system:

Typedef struct {GRealThread thread; GThreadFunc proxy; HANDLE handle;} GThreadWin32

Take a closer look at the first member variable of each structure and see if you find something.

From the perspective of hierarchical relationship, the relationship between these structures is as follows:

Linux platform:

Windows platform:

What does the structure mean in the memory model? Take up a piece of memory space.

All of these data structures put the "child" structure in the first position of the "parent" structure, and it is convenient to cast the type.

In the above memory model, the first part of the GRealThread structure is GThread, so the first part of the memory where the GRealThread is located can be manipulated as a GThread structure variable.

It is more accurate to describe it in object-oriented terms in C++: base class pointers can point to derived class objects.

You can see this in the following code.

3. Creation of threads

(1) function prototype

Platform independent functions (implemented in gthread.c)

GThread * g_thread_new (const gchar * name, GThreadFunc func, gpointer data) GThread * g_thread_new_internal (const gchar * name, GThreadFunc proxy, GThreadFunc func, gpointer data, gsize stack_size, const GThreadSchedulerSettings * scheduler_settings, GError * * error)

Platform related functions (implemented in gthread_posix.c or ghread_win32.c)

GRealThread * g_system_thread_new (GThreadFunc proxy, gulong stack_size, const GThreadSchedulerSettings * scheduler_settings, const char * name, GThreadFunc func, gpointer data, GError * * error)

(2) Linux platform function call chain

Let's first take a look at the function call relationship on the Linux platform:

If you have source code on hand, please pay attention to the func and data parameters in the g_thread_new () function.

Func is the thread execution function passed into the user layer at the beginning, that is, the function that the user creates the thread and finally wants to execute. Data is the function argument received by the func function.

If you are directly facing the Linux operating system programming, when calling the POSIX interface function pthread_create (), it is generally directly passed in the functions and parameters that the user wants to execute.

However, the glib layer does not directly hand over the functions of the user layer to the Linux operating system, but provides two thread proxy functions. When pthread_create () is called, one of these two proxy functions is passed to the operating system according to different circumstances:

The first thread proxy function: g_thread_proxy ()

The second thread proxy function: linux_pthread_proxy ()

Which proxy function is passed depends on whether the macro definition HAVE_SYS_SCHED_GETATTR is valid.

Here is the simplified code for the g_system_thread_new () function:

G_system_thread_new (proxy, stack_size, scheduler_settings, name, func, data, error); GThreadPosix * thread; GRealThread * base_thread; / / fill in the base_thread field, focusing on the following two sentences: base_thread- > thread.func = func; base_thread- > thread.data = data; thread- > scheduler_settings = scheduler_settings; thread- > proxy = proxy # if defined (HAVE_SYS_SCHED_GETATTR) ret = pthread_create (& thread- > system_thread, & attr, linux_pthread_proxy, thread); # else ret = pthread_create (& thread- > system_thread, & attr, (void* (*) (void*)) proxy, thread); # endif

4. Thread execution

Let's assume that the macro definition HAVE_SYS_SCHED_GETATTR is defined and valid, and that pthread_create () in the Linux system receives the linux_pthread_proxy () function.

When the newly created thread is scheduled for execution, the linux_pthread_proxy () function is called to execute:

The simplified linux_pthread_proxy () function:

Static void * linux_pthread_proxy (void * data) {/ / data is the GThreadPosix type pointer in g_system_thread_new, which is platform-dependent. GThreadPosix * thread = data; if (thread- > scheduler_settings) {/ / set thread properties tid = (pid_t) syscall (SYS_gettid); res = syscall (SYS_sched_setattr, tid, thread- > scheduler_settings- > attr, flags);} / / call the thread proxy function in glib, which is g_thread_proxy () return thread- > proxy (data);}

This function focuses on three points:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

The data parameter: it is the GThreadPosix type pointer in the g_system_thread_new function, which is platform-dependent.

The middle part is to set thread properties.

The final return statement calls the first thread proxy function g_thread_proxy in glib.

Continue to post the simplified code for this function:

Gpointer g_thread_proxy (gpointer data) {/ / data is the GThreadPosix type pointer in g_system_thread_new, which is platform-dependent. / / it is forcibly converted to a platform-independent GRealThread type here. GRealThread* thread = data; if (thread- > name) {/ / set thread properties: name g_system_thread_set_name (thread- > name);} / / call the thread entry function of the application layer thread- > retval = thread- > thread.func (thread- > thread.data); return NULL;}

This function only focuses on three points:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Data parameter: the linux_pthread_proxy function passes a pointer of type GThreadPosix, but here it is assigned directly to a pointer of type GRealThread, because their memory model is inclusive.

The middle part is to set the thread name

The final thread- > thread.func (thread- > thread.data) statement calls the function originally passed in by the user and passes the user's data parameter.

At this point, the user-defined threading function user_thread_func (data) is executed.

So, if the glib layer does not define the macro HAVE_SYS_SCHED_GETATTR, then pthread_create () in the Linux system receives the first thread proxy function g_thread_proxy in glib.

The invocation relationship for thread execution is:

5. Windows platform function call chain

Let's first take a look at the function call relationship when creating a thread on the Windows platform:

On the Windows platform, the thread proxy function for glib is g_thread_win32_proxy ().

When the newly created thread is scheduled for execution, the function call relationship is:

IV. Summary

The key to implementing such a threaded function agent design is to use the "parent" structure type variable to convert the "parent" structure type variable into the "child" structure type variable in the C language, because the two are in the memory model. In the first part of the space, the content is exactly the same.

Finally, I combine these diagrams in the article and draw them into the following two diagrams, which fully reflect the thread design ideas in glib:

Linux platform:

Windows platform:

Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report