It's the furthest distance in the world I can't breathe your move and take my heart with me every lonely night
Paint your smile with a paintbrush in my heart I don't doubt your presence is the miracle of my life
Feel your breath every time I want to tell you how much I love you if I say I want to die for you can you leave me in your dreams
Countless throbbing changes in the years you will snuggle in whose bosom the deepest secret is the most fragile beauty in my life
Feel your breath every time I want to tell you how much I love you if I say I want to die for you can you tell me that your heart was the only one
Feel your breath every time I want to tell you how much I love you if I say I want to die for you can you tell me that I was the only one in your heart
This is the melody of today, playing back in my mind, thinking in the mind, struggling when I close my eyes, and seeing the world that can't stay too long when I open our eyes
Because Linux systems provide complex memory management capabilities, this section will explain the programming of memory and I/O access.
In X86, the I/O space is relative to the memory space, accessed through a specific in and out instruction format as follows:
In accumulator, {port number | Dx}out {Port number | DX}, accumulator
Here is the MMU (Memory Management unit), the operating system with the use of the MMU can make users feel as if the program can use very large kernel space, in fact, we usually know the same virtual address. To get a good look at the MMU, let's see two concepts.
The core part of Tlb:mmu, which caches a small number of virtual addresses and the conversion relationships of physical addresses, is the cache of the conversion table.
TTW: When the corresponding address translation relationship is not buffered in the TLB, the corresponding relationship between the virtual address and the physical address needs to be obtained by accessing the in-memory conversion table, and after TTW succeeds, the result should be written to the TLB.
To illustrate the relationship that the MMU uses in memory, you can describe the following relationship
For processors that provide hardware support such as the MMU (Storage Manager, secondary operating system for memory management, and virtual address translation), Linux provides a sophisticated storage management system that allows the process to access up to 4GB of memory. The 4GB memory space of a process is artificially divided into two parts-user space and kernel space. User-space addresses are distributed from 0 to 3GB (page_offset, which equals 0xc0000000 in 0x86), and 3GB to 4GB for kernel space,
In kernel space, the address from 3G to Vmalloc_start is the physical memory map area (which contains the kernel image, the physical page box table Mem_map, and so on) Kmalloc and get_free_page The requested memory is located in the physical memory map area. They are also physically contiguous, with only a fixed offset from the real physical address, so there is a simpler conversion relationship where Virt_to_phys () can convert the kernel virtual address into a physical address:
#define __PA (x) ((unsigned long) (x)-page_offset) extern inline unsigned long virt_to_phys (volatile void * address) {return __PA (address);}
The above conversion process is to subtract 3G (page_offset=0xc000000) from the virtual address. The corresponding function is Phys_to_virt (), which translates the internal nuclear physics address into a virtual address:
#define __VA (x) ((void *) ((unsigned long) (x) +page_offset) extern inline void * PHYS_TO_VIRT (unsigned long address) {Retur n __va (address);}
Both Virt_to_phys () and Phys_to_virt () are defined in include\asm-i386\io.h. The memory requested by Vmalloc is located between Vmalloc_start~vmalloc_end, and there is no simple conversion relationship with the physical address, although they are logically contiguous, but physically they do not require continuous. We use the following procedure to demonstrate the differences between Kmalloc, Get_free_page, and Vmalloc:
#include <linux/module.h> #include <linux/slab.h> #include <linux/vmalloc.h>module_license ("GPL") ; unsigned char *pagemem;unsigned char *kmallocmem;unsigned char *vmallocmem;int __init mem_module_init (void) {// It is best to check whether the application is successful every time the memory request//below this paragraph only as demo code does not check Pagemem = (unsigned char*) get_free_page (0); PRINTK ("<1>pagemem addr=%x", Pagemem); Kmallocmem = (unsigned char*) kmalloc (100, 0); PRINTK ("<1>kmallocmem addr=%x", Kmallocmem); Vmallocmem = (unsigned char*) vmalloc (1000000); PRINTK ("<1>vmallocmem addr=%x", Vmallocmem); return 0;} void __exit mem_module_exit (void) {free_page (PAGEMEM); Kfree (KMALLOCMEM); Vfree (VMALLOCMEM);} Module_init (Mem_module_init); Module_exit (Mem_module_exit);
Devices typically provide a set of registers for controlling devices, reading and writing devices, acquiring device status, and how does the Linux device drive access to the I/O port (register) and I/O memory of the device?
1. Operating I/O port
(1) Request I/O port:
The port should not operate until the drive has not been exclusive of the device. The kernel provides a registration interface to allow the driver to declare the ports it needs:
/* Request_region tells the kernel: N ports to start with first. The parameter name is the device name. If the assigned success return value is non-null, you cannot use the required port (/proc/ioports contains the allocation information for all current ports on the system, and if the request_region assignment fails, you can view the file to see who used the port you want first) */struct Resource *request_region (unsigned long first, unsigned long n, const char *name);
(2) Access IO port:
After the driver is successfully requested to the I/O port, the ports can be read and written. Most hardware separates the 8-bit, 16-bit, and 32-bit ports, and is not as confusing as accessing memory. The driver must call different functions to access ports of different sizes.
The Linux kernel header file (System-dependent header file <asm/io.h>) defines the following inline functions to access the I/O ports:
/* INB/OUTB: Read/write section port (8 bits wide). Some systems define the port parameter as unsigned long, while some platforms define it as unsigned short. The return type of the INB is also dependent on the system's */unsigned inb (unsigned port), void Outb (unsigned char byte, unsigned port),/* INW/OUTW: Read/write port (16-bit width) */ Unsigned inw (unsigned port), void outw (unsigned short word, unsigned port),/* Inl/outl: Read/write 32-bit port. Longword is also dependent on the system, some systems are unsigned long, and some are unsigned int */unsigned inl (unsigned port), void outl (unsigned longword, unsigned port);
(3) Release IO port:
/* After the I/O port is exhausted (possibly when the module is unloaded), you should call Release_region to return the I/O port to the system. The parameters start and N should be passed to request_region consistent with the previous */void release_region (unsigned long start, unsigned long n);
2 Operating IO Memory
(1) Request I/O Memory:
The I/O memory area must be assigned before use. The function interfaces for allocating memory areas are in the <linux/ioport.h> definition:
/* Request_mem_region allocates an I/O memory area that starts at Start,len bytes. The assignment succeeds, returns a non-null pointer, otherwise returns NULL. The system currently all I/O memory allocation information is listed in the/proc/iomem file, when you assign failure, you can look at the file to see who first occupied the memory Area */struct resource *request_mem_region (unsigned long Start, unsigned long len, char *name);
(2) Mapping:
Allocating I/O memory is not the only required step before accessing I/O memory, and you must also ensure that the kernel has access to that I/O memory. Accessing I/O memory is not just a simple dereference pointer, and in many systems I/O memory cannot be accessed directly in this way. Therefore, a mapping must also be set through the Ioremap function.
/* Ioremap is used to map the I/O memory area to the virtual address. The parameter phys_addr is the I/O memory start address to be mapped, the size of the I/O memory to be mapped, and the return value is the virtual address mapped to */void *ioremap (unsigned long phys_addr, unsigned long size);
(3) Access to IO Memory:
After ioremap, the device driver can access any I/O memory address. Note that the address returned by Ioremap cannot be directly dereferenced, instead, the access function provided by the kernel should be used. The correct way to access I/O memory is through a series of functions dedicated to this purpose:
#include <asm/io.h>/* I/O memory read function. The parameter addr should be the address obtained from IOREMAP (which may contain an integer offset); The return value is the value read from the given I/O memory */unsigned int ioread8 (void *addr); unsigned int ioread16 (void *addr); unsigned int ioread32 (void *addr) ; */I/O memory write function. The parameter addr is the same as I/O memory read function, with the parameter value */void Iowrite8 (u8 value, void *addr) to write, void Iowrite16 (U16 value, void *addr), void Iowrite32 ( U32 value, void *addr);/* These functions read and write a series of values to a given I/O memory address, reading from a given buf or writing count values to a given addr. The parameter count represents the number of data to read and write, not the byte size */void ioread8_rep (void *addr, void *buf, unsigned long count), void Ioread16_rep (void *addr, VO ID *buf, unsigned long count), void Ioread32_rep (void *addr, void *buf, unsigned long count), void Iowrite8_rep (void *addr, const void *buf, unsigned long count), void Iowrite16_rep (void *addr, const void *buf, unsigned long count); void Iowrite32_ Rep (void *addr,,onst void *buf,,nsigned long Count);/* Use the following functions when you need to manipulate an I/O address (these functions behave like their C library-like functions): */void memset_io (void *ADDR, U8 value, unsigned int count), void Memcpy_fromio (void *dest, void *source, unsigned int count), void MEmcpy_toio (void *dest, void *source, unsigned int count);/* old I/O memory read and write functions, not recommended for */unsigned readb (address); unsigned readw ( address); unsigned readl (address); void Writeb (unsigned value, address), void Writew (unsigned value, address), void Writel (unsigned value, address);
(4) Release the IO Memory step:
void Iounmap (void * addr); /* Iounmap for releasing mappings that are no longer needed */void release_mem_region (unsigned long start, unsigned long len); /* Iounmap for releasing mappings that are no longer needed */
Almost every peripheral is performed by a register on a read-write device, usually including a control register, a status register, and a data register, where the registers of the peripherals are often continually addressed. Depending on the CPU architecture, there are two ways that the CPU addresses the IO port:
(1) I/O mapping mode (i/o-mapped)
Typically, such as the X86 processor specifically implements a separate address space for peripherals, called "I/O address space" or "I/O port space," the CPU accesses address units in this space through specialized I/O directives such as X86 's in and out directives.
(2) Memory mapping mode (memory-mapped)
The CPU of a RISC instruction system (such as ARM, PowerPC, etc.) typically implements only one physical address space, and the peripheral I/O port becomes part of the memory. At this point, the CPU can access the peripheral I/O port as if it were accessing a memory unit without having to set up specialized peripheral I/O instructions. However, the difference in hardware implementation is completely transparent to the software, and driver developers can treat the I/O port and peripheral memory as a "I/O Memory" resource in the memory-mapped way.
In general, when the system is running, the physical address of the peripheral's I/O memory resources is known and is determined by the hardware design. However, the CPU usually does not have a predefined virtual address range for the physical address of these known peripheral I/O memory resources, and the driver cannot access the I/O memory resources directly through the physical address, but must map them into the core virtual address space (via the page table) before the core virtual address ranges obtained from the mappings are Access these I/O memory resources through an inbound instruction. Linux declares the function Ioremap () in the IO.h header file to map the physical address of the I/O memory resource to the core virtual address space (3GB-4GB), with the following prototype:
void * IOREMAP (unsigned long phys_addr, unsigned long size, unsigned long flags);
The Iounmap function is used to cancel the mapping made by Ioremap () as follows:
void Iounmap (void * addr);
After the physical address of the I/O memory resource is mapped to a core virtual address, we can theoretically read and write the I/O memory resources as read and write Ram. To ensure cross-platform portability of drivers, we should use specific functions in Linux to access I/O memory resources rather than through pointers to the core virtual addresses.
Finally, we want to emphasize the implementation of the MMAP function in the driver. Mapping a device with mmap means that a segment of the user's space is associated with the device's memory, which allows the program to read or write within the allocated address range, which is actually access to the device.
All rights reserved, reprint please specify reprint address: http://www.cnblogs.com/lihuidashen/p/4468223.html
In layman's ~linux device-driven CPU and memory and I/O