In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces how to use C language Setjmp and Longjmp to achieve exception capture and cooperation related knowledge, the content is detailed and easy to understand, the operation is simple and fast, has a certain reference value, I believe that after reading this article on how to use C language Setjmp and Longjmp to achieve exception capture and cooperation will have a harvest, let's take a look at it.
I. Preface
In the C standard library, there are two powerful functions: setjmp and longjmp. Have you ever used them in your code? I asked some of my colleagues in my body that some people don't know these two functions, and some people know this function, but they have never used it.
From the perspective of the scope of knowledge points, the functions of these two functions are relatively simple, which can be explained by a simple example code. However, we need to diverge and think from this knowledge point, associate and compare it with other similar knowledge in this programming language in different dimensions, and compare it with similar concepts in other programming languages. then think about where this knowledge point can be used and how others use it.
For example, we will make a functional comparison between setjmp/longjmp and goto statements, an analogy with the return value of the fork function, and a comparison with co-programs in the Python/Lua language in usage scenarios.
2. Introduction to function grammar
1. The simplest example
Be unreasonable first, just take a look at the simplest sample code. It doesn't matter if you don't understand it, just look familiar:
Int main () {/ / a buffer to temporarily store the environment variable jmp_buf buf; printf ("line1\ n"); / / to hold the context information for the moment int ret = setjmp (buf); printf ("ret =% d\ n", ret) / / check the return value type if (0 = = ret) {/ / return value 0: normal function call returns printf ("line2\ n"); / / actively jump to longjmp at the statement setjmp (buf, 1) } else {/ / return value is non-0: indicates that printf ("line3\ n") is redirected from remote;} printf ("line4\ n"); return 0;}
Execution result:
The order of execution is as follows (if you don't understand, don't dig into it, read the following explanation and then look back):
two。 Function description
First, let's take a look at the signatures of these two functions:
Int setjmp (jmp_buf env); void longjmp (jmp_buf env, int value)
They are all declared in the header file setjmp.h, which Wikipedia explains as follows:
Setjmp: Sets up the local jmp_buf buffer and initializes it for the jump. This routine saves the program's calling environment in the environment buffer specified by the env argument for later use by longjmp. If the return is from a direct invocation, setjmp returns 0. If the return is from a call to longjmp, setjmp returns a nonzero value .
Longjmp:Restores the context of the environment buffer env that was saved by invocation of the setjmp routine in the same invocation of the program. Invoking longjmp from a nested signal handler is undefined. The value specified by value is passed from longjmp to setjmp. After longjmp is completed, program execution continues as if the corresponding invocation of setjmp had just returned. If the value passed to longjmp is 0, setjmp will behave as if it had returned 1; otherwise, it will behave as if it had returned value.
Next, I will use my own understanding to explain the above paragraph in English:
Setjmp function
Function: save all kinds of context information when executing this function, mainly the values of some registers
Parameter: a buffer used to hold context information, which is equivalent to taking a snapshot of the current context information
Return value: there are two kinds of return values. If the setjmp function is called directly, the return value is 0; if the longjmp function is called to jump over, the return value is non-0. Here, you can make an analogy with the function fork that created the process.
Longjmp function
Function: jump to the context (snapshot) saved in the parameter env buffer to execute
Parameter: the env parameter specifies which context (snapshot) to jump to, and value is used to provide return judgment information to the setjmp function, that is, when the longjmp function is called, the parameter value will be used as the return value of the setjmp function.
Return value: no return value. Because when you call this function, you just jump to the code somewhere else to execute it, and it won't come back.
Summary: these two functions are used together to realize the jump of the program.
3. Setjmp: save context information
We know that after C code is compiled into a binary file, it is loaded into memory when it is executed, and CPU fetches each instruction from the code snippet in order to execute it. There are many registers in CPU to hold the current execution environment, such as the code snippet register CS, the instruction offset register IP and, of course, many other registers. We call this execution environment context.
When CPU acquires the next execution instruction, it can obtain the instruction to be executed through the CS and IP registers, as shown below:
Add a few points of knowledge:
In the figure above, the code snippet register CS is treated as a base address, that is, CS points to the start address of the code snippet in memory, and the IP register represents the offset from this base address of the next instruction to be executed. So every time you fetch an instruction, you only need to add the values in the two registers to get the address of the instruction.
In fact, on x86 platforms, the code snippet register CS is not a base address, but a selector. Somewhere in the operating system, there is a table that stores the real start address of the code snippet, while the CS register only stores an index value that points to a table item in the table. This involves the knowledge of virtual memory.
After fetching an instruction, the IP register automatically moves down to the beginning of the next instruction. As for how many bytes to move, it depends on how many bytes are currently taken up by the instruction being fetched.
CPU is a big fool. He doesn't have any ideas. He does what we tell him to do. For example, fetch instructions: as long as we set the CS and IP registers, CPU will use the values in these two registers to get the instructions. If you set these two registers to an incorrect value, CPU will foolishly fetch instructions, but will crash during execution.
We can simply understand these register information as context information, and CPU executes according to these context information. Therefore, the C language prepares the library function setjmp for us to save the current context information and temporarily store it in a buffer.
What is the purpose of preservation? In order to restore to the current place in the future to continue to execute.
There is a simpler example: snapshots in the server. What is the purpose of snapshots? When an error occurs on the server, you can revert to a snapshot!
4. Longjmp: realize the jump
When it comes to jumping, the concept that pops up in my head is the goto statement. I find that many tutorials have a problem with the goto statement and think that you should try not to use it in your code. The point of view is good: if goto is used too much, it will affect the understanding of the order in which the code is executed.
But if you look at the code in the Linux kernel, you can see a lot of goto statements. Again: find a balance between code maintenance and execution efficiency.
Jump changes the execution sequence of the program, goto statements can only jump inside the function, if it is cross-function, it is powerless.
Therefore, the C language provides us with the longjmp function to achieve remote jump, from its name can be seen, that is to say, it can jump across functions.
From the CPU point of view, the so-called jump is to set the various registers in the context to a snapshot at a certain time. Obviously, the above setjmp function has stored the context information (snapshot) at that time in a temporary buffer. If you want to jump to that place to continue execution, just tell CPU directly.
How do you tell CPU? Just overwrite the registers in the temporary buffer with the registers used in CPU.
5. Setjmp: return type and return value
In some programs that require multiple processes, we often use the fork function to "incubate" a new process from the current process, which starts with the next statement of the fork function.
For the main process, the call to the fork function returns and continues to execute the next statement, so how can you tell the difference between the main process and the new process? The fork function provides a return value for us to distinguish:
The fork function returns 0: indicates that this is a new process
The fork function returns a non-0: represents the original main process, and the returned value is the process number of the new process.
Similarly, setjmp functions have different return types. Perhaps it is not accurate to express it in terms of the return type. It can be understood that there are two scenarios when returning from the setjmp function:
When calling setjmp actively: 0 is returned. The purpose of the active call is to save the context and establish a snapshot.
When jumping over via longjmp: non-zero is returned, and the return value is specified by the second parameter of longjmp.
According to the above two different values, we can do different branching processing. When returned through the longjmp jump, different non-zero values can be returned depending on the actual scene. Do friends who have experience in scripting languages such as Python and Lua think of yield/resume functions? They are the same in terms of parameters and return values!
Summary: at this point, basically finished the use of these two functions setjmp/longjmp, I do not know whether my description is clear enough. At this point, take a look at the sample code at the beginning of the article, which should be clear at a glance.
Third, using setjmp/longjmp to realize exception capture.
Since the C function library provides us with this tool, there must be some usage scenarios. Exception trapping is supported directly at the syntax level in some high-level languages (Java/C++), usually try-catch statements, but you need to implement it yourself in C language.
Let's demonstrate the simplest exception capture model, with 56 lines of code:
# include # include typedef int BOOL; # define TRUE 1 # define FALSE 0 / enumeration: error code typedef enum _ ErrorCode_ {ERR_OK = 100, / / No error ERR_DIV_BY_ZERO =-1 / / Divisor 0} ErrorCode; / / buffer jmp_buf gExcptBuf to save the context / / the function typedef int (* pf) (int, int) where an exception may occur; int my_div (int a, int b) {if (0 = = b) {/ / the exception occurs. The second argument is the exception code longjmp (gExcptBuf, ERR_DIV_BY_ZERO) before the function is executed. } / / No exception, return the correct result return a / b;} / / execute the function int try (pf func, int a, int b) that may cause an exception in this function {/ / save the context. If an exception occurs, it will jump here int ret = setjmp (gExcptBuf) If (0 = = ret) {/ / call the number of hats where an exception may occur func (a, b); / / No exception occurred return ERR_OK;} else {/ / exception code return ret;}} int main () {int ret = try (my_div, 8,0) in ret / / exception / / int ret = try (my_div, 8,2); / / No exception if (ERR_OK = = ret) {printf ("try ok!\ n");} else {printf ("try excepton. Error =% d\ n ", ret);} return 0;}
The code does not need to be explained in detail, just look at the comments in the code. This code is only schematic and must require better packaging to be used in production code.
One thing to note: setjmp/longjmp simply changes the order in which the program executes, and if some of the application's own data needs to be rolled back, we need to handle it manually.
Fourth, use setjmp/longjmp to realize the cooperative program.
1. What is a cooperative process?
In C programs, if sequences that need to be executed concurrently are usually implemented by threads, what is a co-program? Wikipedia's explanation of the collaborative process is:
For more detailed information on this page, the web page specifically describes the comparison of the program with threads and generators, and the implementation mechanisms in various languages.
Let's use producers and consumers to briefly experience the difference between processes and threads:
two。 Producers and consumers in threads
Producers and consumers are two sequences of parallel execution, usually executed by two threads
When producers produce goods, consumers are in a state of waiting (blocking). After the production is completed, the consumer is informed to consume the goods through the semaphore.
When consumers consume goods, producers are in a state of waiting (blocking). After the end of consumption, the producer is informed by semaphore to continue to produce goods.
3. Producers and consumers in the process of cooperation
Producers and consumers execute in the same execution sequence, alternating through the jump of the execution sequence
After producing the goods, the producers give up the CPU and let the consumers implement it.
After consuming the goods, consumers give up the CPU and let the producers implement it.
4. Implementation of Cooperative Program in C language
In this paper, a simplest model is given, and the mechanism of realizing the cooperative program through setjmp/longjmp is mainly to understand the execution sequence of the cooperative program, and does not solve the problem of passing parameters and return values.
Typedef int BOOL; # define TRUE 1 # define FALSE 0 / / data structure typedef struct _ Context_ {jmp_buf mainBuf; jmp_buf coBuf;} Context; / / context global variable Context gCtx; / / restore # define resume ()\ if (0 = = setjmp (gCtx.mainBuf))\ {\ longjmp (gCtx.coBuf, 1) \} / / suspend # define yield ()\ if (0 = = setjmp (gCtx.coBuf))\ {\ longjmp (gCtx.mainBuf, 1);\} / / function void coroutine_function (void * arg) {while (TRUE) / / endless loop {printf ("\ nfunctions * coroutine: working\ n") / / simulate the time-consuming operation for (int I = 0; I < 10; + + I) {fprintf (stderr, "."); usleep (1000 * 200);} printf ("\ ncosts * coroutine: suspend\ n"); / / give up CPU yield () }} / / start a co-program / / Parameter 1:func function / / Parameter 2:func required parameters typedef void (* pf) (void *); BOOL start_coroutine (pf func, void * arg) {/ / save the jump point of the main program if (0 = = setjmp (gCtx.mainBuf)) {func (arg); / / call the function return TRUE } return FALSE;} int main () {/ / start a co-program start_coroutine (coroutine_function, NULL); while (TRUE) / / endless loop {printf ("\ nsteps = main: working\ n"); / / simulate a time-consuming operation for (int I = 0; I < 10) + + I) {fprintf (stderr, "."); usleep (1000 * 200);} printf ("\ n CPU = main: suspend\ n"); / / abandon CPU and let the resume ();} return 0;}
The print message is as follows:
If you want to delve into the implementation of cooperative programs in C, you can take a look at the concept of Duff devices, in which branch jumps are implemented using goto and switch statements, and the syntax used is strange but legal.
This is the end of the article on "how to use Setjmp and Longjmp in C language to achieve exception capture and cooperation". Thank you for reading! I believe that everyone has a certain understanding of "how to use Setjmp and Longjmp in C language to achieve exception capture and cooperation". If you want to learn more, you are welcome to follow the industry information channel.
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.