Applying C - Memory Mapped Files
Written by Harry Fairhead   
Tuesday, 26 December 2023
Article Index
Applying C - Memory Mapped Files
Memory Mapping Files
Shared Memory
Semaphore Locking

Every thing is a file - except when it is mapped to memory and when the file in the first place is a memory pseudo file. Confused? It really is simple once you read this extract is from my  book on using C in an IoT context.

Now available as a paperback or ebook from Amazon.

Applying C For The IoT With Linux

  1. C,IoT, POSIX & LINUX
  2. Kernel Mode, User Mode & Syscall
  3. Execution, Permissions & Systemd
    Extract Running Programs With Systemd
  4. Signals & Exceptions
    Extract  Signals
  5. Integer Arithmetic
    Extract: Basic Arithmetic As Bit Operations
  6. Fixed Point
    Extract: Simple Fixed Point Arithmetic
  7. Floating Point
  8. File Descriptors
    Extract: Simple File Descriptors 
    Extract: Pipes 
  9. The Pseudo-File System
    Extract: The Pseudo File System
    Extract: Memory Mapped Files ***NEW
  10. Graphics
    Extract: framebuffer
  11. Sockets
    Extract: Sockets The Client
    Extract: Socket Server
  12. Threading
    Extract:  Pthreads
    Extract:  Condition Variables
    Extract:  Deadline Scheduling
  13. Cores Atomics & Memory Management
    Extract: Applying C - Cores 
  14. Interupts & Polling
    Extract: Interrupts & Polling 
  15. Assembler
    Extract: Assembler

Also see the companion book: Fundamental C

<ASIN:1871962609>

<ASIN:1871962463>

<ASIN:1871962617>

<ASIN:1871962455>

ACcover

The idea that everything is a file doesn’t seem strange until you meet the pseudo file system – also sometimes called the synthetic file system. This is a file system designed to wrap hardware, drivers, system state and just about anything that isn’t a really a file, as a file.

The pseudo file system has folders and files and you can generally work with these as if they were perfectly normal. You can read/write and even use seek to move to a position in such a file. Of course, exactly what you can and cannot do varies according to the object being wrapped as a file or folder.

One of the big problems of the pseudo file system is that it is poorly documented and its behavior can vary a lot depending on the hardware it is implemented on. In this chapter we will look at the Linux pseudo file system. Unix and other operating systems have similar facilities, but there is no standard for the pseudo file system.

In chapter but not in this extract

  • The Linux Pseudo Directories
  • Finding a PID by Name
  • Working with GPIO

Accessing Memory

If you are familiar with programming very simple processors then you will regard direct access to memory as almost a right. If you want to access memory location 0xFFFF then all you have to do is load up a pointer with the address and start making use of it. This is simple, but for most modern processors it isn’t likely to work. The reason is that a modern processor is likely to make use of memory mapping to place physical memory at any logical address it cares to place it. If you try to access memory location 0xFFFF the chances are that it isn’t part of your allocated address space and the result is an error.

So how can you access memory by physical address? The answer is that there is a pseudo file /dev/mem which represents the entire system memory by physical addresses. You can see what is installed at any memory address by reading /proc/iomem:

cat /proc/iomem.

The pseudo file /dev/mem is a character device file that is an image of the main memory. When you read or write byte n this is the same as reading and writing the memory location at physical byte address n. You can move the pointer to almost any memory location using lseek and read and write blocks of bytes using fread and fwrite, the exceptions being areas of memory inaccessible for security reasons. It is simple, but it takes some time to get used to the idea. For example to open the file to read and write it you might use:

int memfd = open("/dev/mem", O_RDWR | O_SYNC);

The O_RDWR opens the file for read and write and the O_SYNC flag makes the call blocking. After this you can lseek to the memory location you want to work with. For example, to go to the start of the GPIO registers in a Raspberry Pi 2 or 3 you would use:

uint32_t p = lseek(memfd, (off_t) 0x3f200000, SEEK_SET);

and for the Pi 1 and Pi Zero:

uint32_t p = lseek(memfd, (off_t) 0x20200000, SEEK_SET);

Notice that these are the base address plus 0x200000. Next you could read the 32 bits starting at that location, i.e. the FSEl0 register:

int buffer[1];
int n = read(memfd, buffer, 4);

It is worth mentioning that not all processors make use of memory-mapped I/O. In particular the Intel x86 family makes use of some memory-mapped I/O and a separate port-based system. The I/O ports behave like a completely separate address and memory bus and no memory mapping is applied. If you want to work with this entirely separate set of ports then you can make use of the /dev/port pseudo file, which works in exactly the same way as /dev/mem. You can see what is installed at what address by reading the /proc/ioports pseudo file:

cat /proc/ioports

 



Last Updated ( Wednesday, 27 December 2023 )