_ User & address_space (1)

Source: Internet
Author: User

All the kernel code basically contains the Linux/compile. h file. Therefore, it is the foundation. I plan to analyze the code in this file first, and analyze other code when I have time.

First, we can see the macro _ Assembly _. This variable is actually added to the compilation code by the compiler using a parameter such as-D, the aflags variable also defines this variable. GCC will define this macro as 1. It is used here because the compilation Code does not use attributes like _ User (this article will discuss how to return sub-tasks for attributes like _ User ), this attribute is added when the function is defined, so as to avoid unnecessary reference during compilation of assembly code.
Next we will make a judgment on the macro _ checker _. There are many things to talk about here.
When you compile the kernel code and use make C = 1 or C = 2, a sparse tool is called. How can this tool check the kernel code, checks kernel functions or variables that declare the features recognized by the sparse tool. When the sparse tool is called, # DEFINE _ checker _ 1 is added to the sparse code. In other words, if you use sparse to check the code, the kernel code defines the _ checker _ macro, otherwise it is not defined.
So we can see that attributes like _ attribute _ (noderef, address_space (1) are recognized by the sparse tool.
So what are these attributes used for? I will introduce them one by one.
Some of these attributes have not been added to the GCC documentation. At least I didn't see them in the GCC 4.3.2 feature. Some online friends asked similar questions, greg answered him and then complained about Greg's document. Greg told him that if he had time to complain, it would be better to update the document by himself. He cannot have such high requirements on the documentation of a free tool unless he pays.

# DEFINE _ User _ attribute _ (noderef, address_space (1 )))

_ User, that is, _ attribute _ (noderef, address_space (1), is used to modify a variable. This variable must be a non-release reference (no dereference) that is, the variable address must be valid, and the address space of the variable must be 1, that is, the user program space.
The program space is divided into three parts. 0 indicates normal space, that is, normal address space. For kernel code, it is of course the kernel space address. 1 indicates the user address space. There is another 2, indicating the device address ing space. For example, the address space mapped by the hardware device register in the kernel.

Therefore, there is a copy_to_user function in the kernel function. This method is used for Parameter definition of the function. Of course, this feature check only works when the sparse tool is installed on the machine and called during compilation.

# DEFINE _ KERNEL/* default address space */

According to the definition, it is the default address space, that is, 0. I want to define it as _ attribute _ (noderef, address_space (0.

# DEFINE _ safe _ attribute _ (SAFE ))

This definition also exists in sparse. The kernel code was added by Linus when 2.6.6-RC1 was changed to 2.6.6-RC2. After my hard work, I finally found the cause, the reason why Linus needs to add this definition is as follows:
Some people found that during code compilation, the compiler was a little harsh on variable check, which caused the code to always go wrong during compilation (I didn't check whether the compilation failed or there was warning information here, because the current compiler is no longer the current year's compiler, and the code is not the current year's Code ). For example,
Int test (struct a * a, struct B * B, struct C * c ){
Return a-> A + B-> B + C-> C;
}
This compilation may cause problems because the system does not check whether the parameter is null, so it can be called directly. However, there are many functions in the kernel. When they are called, these parameters must not be empty, so they do not need to be checked for non-empty parameters, therefore, a _ safe attribute is added. If the variable is declared in this way,
Int test (struct a * _ safe a, struct B * _ safe B, struct C * _ safe C ){
Return a-> A + B-> B + C-> C;
}
Compilation is fine.

However, I have not found any use of the definition of _ safe in my current code. I don't know if the compiler currently supports this special situation, so you don't need to add such code.

# DEFINE _ force _ attribute _ (Force ))

It indicates that the defined variable type can be forced type conversion. During sparse analysis, no warning information is reported.

# DEFINE _ nocast _ attribute _ (nocast ))

This indicates that the parameter type of this variable must be correct with the actual parameter type, or generate alarm information during sparse.

# DEFINE _ iomem _ attribute _ (noderef, address_space (2 )))

This definition is the same as _ User and _ User, except that the variable address needs to be mapped to the device address space.

# DEFINE _ acquires (x) _ attribute _ (context (x, 0, 1 )))
# DEFINE _ Releases (x) _ attribute _ (context (x, 1, 0 )))

This is a pair of interrelated function definitions. The first sentence indicates that the reference count of parameter X must be 0 before execution. After execution, the reference count must be 1, and the second sentence is the opposite, this definition is used to modify the variables defined by the function.

# DEFINE _ acquire (x) _ context _ (x, 1)
# DEFINE _ release (x) _ context _ (x,-1)

This is a pair of interrelated function definitions. The first sentence indicates to increase the Count of variable X. The addition is 1, and the second sentence is the opposite. This is used for function execution.

If the above four sentences are unbalanced in the code, an alarm will be reported during sparse detection. Of course, sparse detection is only a means, and it is a means of static code check. Therefore, it has limited help and may trigger an alarm when the correct code is regarded as incorrect. If you still don't know the meaning of the above four sentences, you can find the usage of the symbols in the source code. In essence, the first group and the second group are no different. They are only used in different locations.

# DEFINE _ cond_lock (x, c) (c )? ({__ Acquire (x); 1 ;}): 0)

The condition lock is used. When the value of C is not 0, add 1 to the Count value and return 1. But here I have a question: here, there is a _ cond_lock definition, but the corresponding _ cond_unlock is not defined, so there is no way to achieve consistency in the release of variables. Also, I checked the definition of the spin_trylock () function. It uses the _ cond_lock function and the _ spin_trylock function. In the _ spin_trylock function, after several calls, the _ acquire function will be used. In this case, it is equivalent to an operation, and two computations will be performed, which will lead to alarms in sparse detection, I wrote code to conduct an experiment and verified my judgment. There was indeed an alarm. If I wrote the UNLOCK Command twice, there would be no alarm information, however, this is inconsistent with the running of the program.

Extern void _ chk_user_ptr (const volatile void _ User *);
Extern void _ chk_io_ptr (const volatile void _ iomem *);

These two statements are interesting. The function is defined here, but there is no function implementation in the code. The purpose of doing so is to let sparse perform necessary parameter type checks for the code During sparse. In the actual compilation process, the implementation of these two functions is not required.

# Define notrace _ attribute _ (no_instrument_function ))

This statement defines an attribute that can be used to modify a function and specify that the function is not tracked. So how does this attribute return? Originally, the GCC compiler implements a very powerful function. If you open a corresponding option during compilation, you can complete the program execution, some tools are used to display the entire function call process, so that programmers do not need to manually add statements that can display the function call process in all functions. This consumes a lot of time and effort, it is also prone to errors. In terms of applications, graphviz can be used for display. You can check the usage instructions and software implementation principles on the Internet, which is easy to find. It corresponds to the kernel. Because the kernel is always in the running stage, this set of things cannot be used. The kernel implements a ftrace mechanism within itself. when compiling the kernel, if this option is enabled, you can mount a debugfs File System to display the corresponding content. For detailed operation steps, see the documentation in the kernel source code. So much has been said above, and what is the relationship with the notrace attribute? Because two special functions are used during the display of the function call process. Before the function is called and the function is returned after execution, these two special functions are called respectively. If you do not specify the functions of these two functions as the attributes that are not tracked, the entire tracing process will fall into an infinite loop.

# Define likely (x) _ builtin_exact CT (!! (X), 1)
# Define unlikely (x) _ builtin_exact CT (!! (X), 0)

These two sentences are a correspondence relationship. _ Builtin_ct CT (expr, c) is supported by the new version of GCC. It is used for code optimization and is used to tell the compiler that the expr period is very likely C, in this way, when GCC generates the corresponding assembly code, it will put all the code that may be executed together, so that the code can be skipped. Why can this improve the CPU execution efficiency? This is because the CPU has a mechanism for obtaining commands in advance during execution. It extracts some of the commands to be executed and prepares them for execution. The CPU does not know the logic of the program, so they are all pulled from the program. If this time can be skipped, the CPU can use the commands pre-extracted. Otherwise, the commands obtained in advance are useless. Note that the previous versions do not exist in the definition of _ builtin_expect !! The role of this symbol is actually negative. Why? It is to ensure that the value of non-zero X is later 1. If the value of zero is later 0, it is only 0.

# Ifndef barrier
# Define barrier () _ memory_barrier ()
# Endif

If the barrier function is not defined, the barrier () function is defined as _ memory_barrier (). But in the kernel code, it will contain the compiler-gcc.h file, so in this file, the barrier () is defined as _ ASM _ volatile __(""::: "Memory "). Barrier is a barrier. Why is it a barrier? This is because the CPU may execute some commands in the way that it deems optimal to optimize the commands during execution, the execution sequence is not necessarily written by the program in the source code. The compiler may also make some optimizations when generating binary commands. In this way, problems may occur in the execution of multi-CPU, multi-thread, or mutex locks. The memory barrier can be regarded as a line. The memory barrier is used here to ensure that the Operation above the barrier does not affect the operation below the barrier. Then let's see how this barrier works. _ ASM _ indicates that all the subsequent items are Assembly commands. Of course, this is a method to embed assembly in C language, and its syntax has its particularity, I will only talk about this command here. _ Volatile _ indicates that the Assembly command here is not optimized, which ensures the correctness of the code here. "" Indicates that it is an empty command. Since it is an empty command, no input or output is required for the corresponding command. In GCC, it is stipulated that if the assembly is embedded in this way, if the output does not exist, two colons are required to replace the position of the output operand. Therefore, two values must be added ::, the command is ""::. Then add the colon to separate the input and the input over the sky, that is "":::. Memory is a special syntax in GCC. When it is added, the GCC compiler will generate an action that prevents GCC from retaining the memory value in the register, in addition, the corresponding memory is not optimized for storage and loading, and no additional code is generated for this action. This behavior is ensured by the GCC compiler. If you are more interested in this article, you can check the gcc's help document and the article titled memory-barriers.txt in the internal section.

# Ifndef reloc_hide
# Define reloc_hide (PTR, Off )/
({Unsigned long _ PTR ;/
_ PTR = (unsigned long) (PTR );/
(Typeof (PTR) (_ PTR + (off ));})
# Endif

There is nothing worth talking about. You can also understand it. Although you don't know where to use it, let's talk about it later.

Many of the subsequent definitions are not implemented. You can see the annotations, so we will not talk about them here. Alas, but let's just insert another sentence, __the implementation of the deprecated attribute is deprecated.

# Define noinline_for_stack noinline

# Ifndef _ always_inline
# DEFINE _ always_inline inline
# Endif

Here, the noinline and inline attributes are two opposite attributes, which are well understood in terms of meaning.

# Ifndef _ cold
# DEFINE _ cold
# Endif

From the annotations, we can see that if the attribute of a function is _ cold, the compiler will think that this function is almost impossible to be called. During code optimization, this will be taken into account. However, I did not see the description that this attribute is supported in GCC.

# Ifndef _ Section
# DEFINE _ Section (s) _ attribute _ (_ Section _ (# s )))
# Endif

This is easy to understand. It is used to modify the region in which a function is stored without the default compiler method. The name of this region is obtained by the definer. The format is _ Section _ and the user input parameter.

# Define access_once (x) (* (volatile typeof (x) *) & (x ))

The definition of this function is very interesting. It is to access the corresponding things of this X parameter once. It does this: first obtain the address of this X and then convert the address, convert to a pointer pointing to this address type, and then obtain the content pointed to by this pointer. In this way, access is achieved once, haha.

It's really not easy. I finally finished writing this thing. There are only a few dozen lines of code, and the knowledge it contains is really rich. By analyzing this header file, I learned a lot by myself, don't dare to share it with friends who are interested.

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.