The Linux system implements most of the interfaces between user-mode processes and hardware devices by issuing system calls to the kernel.
System calls are services provided by the operating system. User programs use various system calls to reference various services provided by the kernel. The execution of system calls causes user programs to fall into the kernel, and the falling action is completed by the swi soft interrupt.
1. Users can use system calls in two ways:
The first way is through C library functions, including system call encapsulation functions and other common functions in the C library.
The second way is to use the _syscall macro. In kernels prior to version 2.6.18, there are seven _syscall macros defined in the include/asm-i386/unistd.h file, namely:
_syscall0(type,name) _syscall1(type,name,type1,arg1) _syscall2(type,name,type1,arg1,type2,arg2) _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6)
Among them, type represents the return value type of the generated system call, name represents the name of the system call, typeN and argN represent the type and name of the Nth parameter respectively, and their number is the same as the number after _syscall.
The purpose of these macros is to create a function named name, and the number following _syscall indicates the number of parameters of the function.
For example, the sysinfo system call is used to obtain overall system statistics, and is defined using the _syscall macro:
_syscall1(int, sysinfo, struct sysinfo *, info);
The expanded form is:
int sysinfo(struct sysinfo * info) { long __res; __asm__ volatile("int $0x80" : "=a" (__res) : "0" (116),"b" ((long)(info))); do { if ((unsigned long)(__res) >= (unsigned long)(-(128 + 1))) { errno = -(__res); __res = -1; } return (int) (__res); } while (0); }
It can be seen that _syscall1(int, sysinfo, struct sysinfo *, info) is expanded into a function named sysinfo. The original parameter int is the return type of the function, and the original parameters struct sysinfo * and info constitute the parameters of the new function respectively.
Use the _syscall macro to define the required system call in the program file, and then you can call the system call directly through the system call name in the following code. The following is an example of using the sysinfo system call.
Code Listing 5.1 Example of using the sysinfo system call
#include #include #include #include /* for struct sysinfo */ _syscall1(int, sysinfo, struct sysinfo *, info); int main(void) { struct sysinfo s_info; int error; error = sysinfo(&s_info); printf("code error = %d/n", error); printf("UpTIme = %lds/nLoad: 1 min %lu / 5 min %lu / 15 min %lu/n" "RAM: total %lu / free %lu / shared %lu/n" "Memory in buffers = %lu/nSwap: total %lu / free %lu/n" "Number of processes = %d/n", s_info.upTIme, s_info.loads[0], s_info.loads[1], s_info.loads[2], s_info.totalram, s_info.freeram, s_info.sharedram, s_info.bufferram, s_info.totalswap, s_info.freeswap, s_info.procs); exit(EXIT_SUCCESS); }
However, since version 2.6.19, the _syscall macro has been abolished, and we need to use the syscall function to call the system call by specifying the system call number and a set of parameters.
The syscall function prototype is:
int syscall(int number, ...);
Where number is the system call number, followed by all the parameters of the system call in sequence. The following is an example of calling the getTId system call.
Code Listing 5.2 Example of using the getTId system call
#include #include #include #define __NR_gettid 224 int main(int argc, char *argv[]) { pid_t tid; tid = syscall(__NR_gettid); }
Most system calls include a SYS_ symbol constant to specify their mapping to the system call number, so line 10 above can be rewritten as:
tid = syscall(SYS_gettid);
2 Differences between system calls and application programming interfaces (APIs)
The difference between an application programming interface (API) and a system call is that the former is just a function definition that describes how to get a given service, while the latter is an explicit request to the kernel via a software interrupt. The POSIX standard is for APIs, not for system calls. Unix systems provide programmers with many API library functions. Some of the APIs defined by the standard C library libc refer to wrapper routines (whose sole purpose is to issue system calls). Usually, there is one wrapper routine for each system call, and the wrapper routines define the API used by the application. The reverse is not true, and an API does not necessarily correspond to a specific system call. From the programmer's point of view, the difference between APIs and system calls is irrelevant: the only relevant things are the function name, the types of arguments, and the meaning of the return code. However, from the kernel designer's point of view, the difference does matter, because system calls belong to the kernel, while user-mode library functions do not belong to the kernel.
Most of the wrapper routines return an integer whose meaning depends on the corresponding system call. A return of -1 usually means that the kernel cannot satisfy the process's request. Failure of a system call handler may be caused by invalid parameters, lack of available resources, hardware problems, etc. The errno variable defined in the libd library contains the specific error code. Each error code is defined as a constant macro.
When a process in user mode calls a system call, the CPU switches to kernel mode and begins executing a kernel function. Because the kernel implements many different system calls, the process must pass a parameter called the system call number to identify the desired system call. All system calls return an integer value. These return values are different from the conventions for return values of wrapper routines. In the kernel, an integer or 0 indicates that the system call has completed successfully, while a negative number indicates an error condition. In the latter case, this value is a negative error code that must be returned to the application in the errno variable.
3 System call execution process
ARM Linux system uses SWI instruction to enter kernel space from user space. Let's first understand this SWI instruction. SWI instruction is used to generate software interrupt, so as to realize the transition from user mode to supervisor mode. CPSR is saved to SPSR of supervisor mode, and execution is transferred to SWI vector. SWI instruction can also be used in other modes, and the processor switches to supervisor mode in the same way. The instruction format is as follows:
SWI{cond} immed_24
in:
immed_24 24-bit immediate value, the value is an integer between 0 and 16777215.
When using the SWI instruction, the following two methods are usually used for parameter passing. The SWI exception handler can provide related services. Both methods are user software agreements. The SWI exception interrupt handler must obtain a 24-bit immediate value by reading the SWI instruction that caused the software interrupt.
1) The 24-bit immediate value in the instruction specifies the type of service requested by the user, and the parameters are passed through general registers. For example:
MOV R0,#34SWI 12
2) The 24-bit immediate value in the instruction is ignored. The service type requested by the user is determined by register R0 only, and the parameters are passed through other general registers. For example:
MOV R0, #12MOV R1, #34SWI 0
In the SWI exception handler, the steps to remove the SWI immediate value are: first determine whether the SWI instruction of a soft interrupt is an ARM instruction or a Thumb instruction, which can be obtained by accessing the SPSR; then obtain the address of the SWI instruction, which can be obtained by accessing the LR register; then read the instruction and decompose the immediate value (lower 24 bits).
Entering system call from user space
Normally, the code we write calls the system call through the encapsulated C lib. Take the open function in the 0.9.30 version of uClibc as an example to trace how this encapsulated function calls the system call step by step. It is defined in include/fcntl.h:
# define open open64
open is actually just an alias for open64.
In libc/sysdeps/linux/common/open64.c you can see:
extern __typeof(open64) __libc_open64;extern __typeof(open) __libc_open;
It can be seen that open64 is just an alias of __libc_open64, and the __libc_open64 function is defined in the same file:
libc_hidden_proto(__libc_open64)int __libc_open64 (const char *file, int oflag, ...){ mode_t mode = 0; if (oflag & O_CREAT) { va_list arg; va_start (arg, oflag); mode = va_arg (arg, mode_t); va_end (arg); } return __libc_open(file, oflag | O_LARGEFILE, mode);}libc_hidden_def(__libc_open64)
Finally, __libc_open64 calls the __libc_open function, which is defined in the file libc/sysdeps/linux/common/open.c:
libc_hidden_proto(__libc_open)int __libc_open(const char *file, int oflag, ...){ mode_t mode = 0; if (oflag & O_CREAT) { va_list arg; va_start (arg, oflag); mode = va_arg (arg, mode_t); va_end (arg); } return __syscall_open(file, oflag, mode);}libc_hidden_def(__libc_open)
_syscall_open is defined in the same file:
static __inline__ _syscall3(int, __syscall_open, const char *, file, int, flags, __kernel_mode_t, mode)
In the file libc/sysdeps/linux/arm/bits/syscalls.h, you can see:
#undef _syscall3#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) type name(type1 arg1,type2 arg2,type3 arg3) { return (type) (INLINE_SYSCALL(name, 3, arg1, arg2, arg3)); }
This macro actually completes the work of defining a function. The first parameter of this macro is the return value type of the function, the second parameter is the function name, and the subsequent parameters are the function parameter types and parameter names as indicated by its parameter name. __syscall_open is actually:
Previous article:ARM assembly and C mixed programming
Next article:Detailed introduction to building a cross-compilation toolchain for ARM Linux
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- Sn-doped CuO nanostructure-based ethanol gas sensor for real-time drunk driving detection in vehicles
- Design considerations for automotive battery wiring harness
- Do you know all the various motors commonly used in automotive electronics?
- What are the functions of the Internet of Vehicles? What are the uses and benefits of the Internet of Vehicles?
- Power Inverter - A critical safety system for electric vehicles
- Analysis of the information security mechanism of AUTOSAR, the automotive embedded software framework
- Free resource download | Five trends driving change in power management
- My Journey of MCU Development (Part 2)
- 【Development and application based on NUCLEO-F746ZG motor】13. Parameter configuration - USART3 configuration
- What is the sound-generating mechanism of a piezoelectric buzzer? How to choose the sound component?
- The mechanical and electrical parts of the intelligent car based on the DFRobot four-way motor driver board have been preliminarily completed.
- [Example] Easily implement wifi remote RS485 bus (multi-node bus)
- Forum friends give examples to explain these operators in C language
- Talk about the upper and lower tubes of the buck circuit_DC-DC BUCK bootstrap circuit
- Real-time solution to single chip microcomputer delay
- CB5654 Development Board Review 2