For uClinux, it is designed for a processor without MMU and cannot use the virtual memory management technology of the processor. UClinux still uses paging Management of memory, and the system paging the actual memory at startup. The program is loaded by page when the application is loaded. However, since MMU management is not available, uClinux uses a real-memory management policy. In uClinux, memory access is direct, and the addresses accessed in all programs are physical addresses. The operating system does not protect the memory space. Each process actually shares a runtime space. Before a process is executed, the system must allocate sufficient contiguous address space for the process and load all the data into the contiguous space of the primary memory.
An operation without memory protection will result in an address error even if an Invalid Pointer is called by a non-privileged process, and may cause program crashes, or even system suspension. Obviously, the code running on such a system must be carefully programmed and thoroughly tested to ensure robustness and security.
Processes loaded by the kernel must run independently, regardless of their location in the memory. The first way to achieve this is that once the program is loaded into RAM, the baseline address of the program will be "fixed; another method is to generate code that uses only relative addressing (known as "location-independent code", position independent code, or PIC for short ). UClinux supports both modes.
Elf can generate a special code-position-independent code, Pic ). The user uses-FPIC for GCC to instruct the GNU compilation system to generate the PIC code. It is the basis for implementing shared libraries or sharing executable code. the special feature of this Code is that it can be loaded to any address in the memory address space for execution. this is also a convenient way for the loader to dynamically link shared libraries in the process.
The implementation of PIC applies the fact that the distance between any instruction in the code segment and any variable in the data segment is a constant irrelevant to the absolute storage location of the code segment and the data segment. Therefore, the compiler creates a table at the beginning of the Data Segment, called the Global Offset Table. Got ). Got contains the table category of the global data target referenced by this target module. The compiler also generates a relocation record for each table in got. During loading, the dynamic linker relocates each table in the got so that it contains the correct absolute address. The PIC code indirectly references each global variable through got in the Code. In this way, simple data references in the Code become complicated and you must add instructions to get the appropriate got table content. The reference to read-only data is also based on the same principle. Therefore, the Code Compiled by an IC is overhead than the general code.
Frequent dynamic memory allocation can cause memory fragmentation and may exhaust system resources. For applications that use dynamic memory allocation, one way to improve robustness is to replace malloc () calls with a pre-allocated buffer pool (preallocated buffer pool. Since uClinux does not use virtual memory, page switching between inbound and outbound memory is not implemented, because the page cannot be loaded to the same location in Ram.
The uClinux target board processor lacks hardware units for memory management, which requires some changes to the Linux system interface. The biggest possible difference is that fork () and BRK () systems are not called. Call fork () to copy the process to create a child process. In Linux, fork () is implemented using the copy-on-write page. Without MMU, uClinux cannot completely and reliably copy a process or access copy-on-write. To make up for this defect, uClinux implements vfork (). When the parent process calls vfork () to create a child process, the two processes share all their memory space, including the stack. The child process either runs in place of the parent process (at this time, the parent process has sleep) until the child process calls exiti () to exit, or calls exec () to execute a new process, in this case, an executable file is generated. This process cannot be avoided even if it is just a copy of the parent process. After the child process executes exit () or exec (), the child process uses wakeup to wake up the parent process, and the parent process continues to run.
UClinux does not have an automatic growth stack or BRK () function. In this way, the user space program must use the MMAP () command to allocate memory. For convenience, the implemented malloc () in the C language library of uClinux is essentially an MMAP (). You can specify the stack size of a program during compilation.
Summary: During the Process of porting an application to uClinux and writing code, we will always focus on these features:
1. If possible, choose-Disable-shared and-enable-static When configuring.
2. Change all fork () in the source code to vfork ();
3. Add-WL and-elf2flt to the cross compiler and compilation options in makefile. Although this is only a link option, I have carefully specified this option in ldflags and cflags, and even in CC.
The change option is to convert the ELF format to the flat format recognized by uClinux. In this conversion process, we cannot use strip to remove some information from the ELF file, or use the-O2 option to optimize the code. Some removed information may cause problems in running the final flat format file.