mmap /dev/fb0 fails with "Invalid argument" - linux

I have an embedded system and want to use /dev/fb0 directly. As a first test, I use some code based on example-code found everywhere in the net and SO. Opening succeeds, also fstat and similar. But mmap fails with EINVAL.
Source:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
int main() {
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;
int x = 0, y = 0;
long int location = 0;
// Open the file for reading and writing
fbfd = open("/dev/fb0", O_RDWR);
if (fbfd == -1) {
perror("Error: cannot open framebuffer device");
exit(1);
}
printf("The framebuffer device was opened successfully.\n");
struct stat stat;
fstat(fbfd, &stat);
printf("/dev/mem -> size: %u blksize: %u blkcnt: %u\n",
stat.st_size, stat.st_blksize, stat.st_blocks);
// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
perror("Error reading fixed information");
exit(2);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
perror("Error reading variable information");
exit(3);
}
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
// Figure out the size of the screen in bytes
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
const int PADDING = 4096;
int mmapsize = (screensize + PADDING - 1) & ~(PADDING-1);
// Map the device to memory
fbp = (char *)mmap(0, mmapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if ((int)fbp == -1) {
perror("Error: failed to map framebuffer device to memory");
exit(4);
}
printf("The framebuffer device was mapped to memory successfully.\n");
munmap(fbp, screensize);
close(fbfd);
return 0;
}
Output:
The framebuffer device was opened successfully.
/dev/mem -> size: 0 blksize: 4096 blkcnt: 0
640x480, 4bpp
Error: failed to map framebuffer device to memory: Invalid argument
strace:
...
open("/dev/fb0", O_RDWR) = 3
write(1, "The framebuffer device was opene"..., 48The framebuffer device was opened successfully.
) = 48
fstat64(3, {st_mode=S_IFCHR|0640, st_rdev=makedev(29, 0), ...}) = 0
write(1, "/dev/mem -> size: 0 blksize: 409"..., 44/dev/mem -> size: 0 blksize: 4096 blkcnt: 0
) = 44
ioctl(3, FBIOGET_FSCREENINFO or FBIOPUT_CONTRAST, 0xbfca6564) = 0
ioctl(3, FBIOGET_VSCREENINFO, 0xbfca6600) = 0
write(1, "640x480, 4bpp\n", 14640x480, 4bpp
) = 14
old_mmap(NULL, 155648, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = -1 EINVAL (Invalid argument)
write(2, "Error: failed to map framebuffer"..., 49Error: failed to map framebuffer device to memory) = 49
write(2, ": ", 2: ) = 2
write(2, "Invalid argument", 16Invalid argument) = 16
write(2, "\n", 1
) = 1
The boot-screen with console and tux is visible. And cat /dev/urandom > /dev/fb0 fills the screen with noise. The pagesize is 4096 on the system (`getconf PAGESIZE). So, 155648 (0x26000) is a multiple. Offset and pointer are both zero. Mapping and filemode are both RW .. what am I missing?
This is for an embedded device build with uClibc and busybox running a single application and I have to port it from an ancient kernel. There is code for linedrawing and such and no need for multiprocessing/ windowing .. please no hints to directfb ;).

The kernel driver that presents the framebuffer doesn't support the legacy direct mmap() of the framebuffer device; you need to use the newer DRM and KMS interface.

Related

Can't read frame buffer colormap information using ioctl on raspberry pi

I am working on Raspberry Pi 4, with raspios bullseye-arm-64-lite (version 11).
I want to configure the frame buffer to 8-bits per pixel and use a custom colormap.
I configured the frame buffer to 8-bits per pixel. I could read the fixed and variable information about the frame buffer correctly using ICOTL functions in my C code, but could not read colormap information. While trying to read colormap info, the IOCTL call returns -1.
I added the user to video group and set the frame buffer to 1280x1024 resolution with 8-bit bitdepth using fbset command as follows.
pi#raspberrypi:~ $ fbset -fb /dev/fb0 -g 1280 1024 1280 1024 8
Here is the output of fbset -i command.
pi#raspberrypi:~ $ fbset -i
mode "1280x1024"
geometry 1280 1024 1280 1024 8
timings 0 0 0 0 0 0 0
rgba 8/0,8/0,8/0,0/0
endmode
Frame buffer device information:
Name : BCM2708 FB
Address : 0x3ea87000
Size : 1310720
Type : PACKED PIXELS
Visual : PSEUDOCOLOR
XPanStep : 1
YPanStep : 1
YWrapStep : 0
LineLength : 1280
Accelerator : No
Here is the my C code.
#include <cstdio>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
static struct fb_fix_screeninfo fb_fix;
static struct fb_var_screeninfo fb_var;
static int fb;
static unsigned short ored[256], ogreen[256], oblue[256];
static struct fb_cmap ocmap = { 0, 256, ored, ogreen, oblue };
int main()
{
int fp = open ("/dev/fb0", O_RDWR | O_CLOEXEC);
if (fp < 0){
printf("Error : Can not open framebuffer device/n");
return -1;
}
// Get fixed info about fb
if (ioctl(fp,FBIOGET_FSCREENINFO,&fb_fix)){
printf("Error reading fixed information/n");
return -1;
}
// Get variable info about fb
if (ioctl(fp,FBIOGET_VSCREENINFO,&fb_var)){
printf("Error reading variable information/n");
return -1;
}
printf("--- FB info ---\n");
printf("FB_VISUAL : %u \nFB_BITS_PER_PIXEL : %u \n\n", fb_fix.visual, fb_var.bits_per_pixel);
// Get info about cmap
int i = ioctl(fp,FBIOGETCMAP,&ocmap);
if (i == -1){
printf("Error reading cmap information\n");
return -1;
}
return 0;
}
And, here is the output of C code.
--- FB info ---
FB_VISUAL : 3
FB_BITS_PER_PIXEL : 8
Error reading cmap information
After looking at many sources including fbida, trinity and this, I believe that I am using the IOCTL calls in right way, but still can't figure out why "ioctl(fp,FBIOGETCMAP,&ocmap);" returns -1.
I would greatly appreciate any help in this direction.

what is the right inotify event mask configuration for monitoring writes to a file?

I am trying to use inotify to monitor a file for any reads or writes.
typical use case would be user runs one of the following commands and I get the appropriate notification.
echo "xyz" > /tmp/the_file # This should give me a write notification
OR
cat /tmp/the_file # This should give me an access notification.
I have tried the following masks :
mask = (IN_MODIFY|IN_ACCESS); //gets correct notification for reads , for writes i get 2 notifications
mask = (IN_CLOSE_WRITE|IN_ACCESS); // gets correct notification for reads , no notification for writes.
what would be the right mask value to get a single notification for every read and write ?
I am using the test application from this blog as reference :
How to Use inotify API in C Language
#include<stdio.h>
#include<sys/inotify.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<fcntl.h>
#define MAX_EVENTS 1024
#define LEN_NAME 256
#define EVENT_SIZE (sizeof (struct inotify_event))
#define BUF_LEN (MAX_EVENTS * (EVENT_SIZE + LEN_NAME))
int fd,wd;
void sig_handler(int sig){
inotify_rm_watch(fd, wd);
close(fd);
exit(0);
}
int main(int argc, char **argv){
char *path_to_be_watched;
int i=0,length;
char buffer[BUF_LEN];
struct inotify_event *event = (struct inotify_event *) &buffer[i];
path_to_be_watched = argv[1];
signal(SIGINT,sig_handler);
fd = inotify_init();
fcntl(fd, F_SETFL, O_NONBLOCK);
wd = inotify_add_watch(fd, path_to_be_watched, IN_MODIFY | IN_ACCESS);
if(wd==-1){
printf("Could not watch : %s\n", path_to_be_watched);
} else {
printf("Watching : %s\n", path_to_be_watched);
}
while(1){
i=0;
length = read(fd,buffer,BUF_LEN);
while(i<length){
event = (struct inotify_event *) &buffer[i];
if(event->len){
if ( event->mask & IN_MODIFY ) {
printf( "The file %s was modified.\n", event->name );
/* This gets printed twice when I run 'echo "123" > /tmp/this_file' */
}
else if ( event->mask & IN_ACCESS ) {
printf( "The file %s was accessed.\n", event->name );
}
}
i += EVENT_SIZE + event->len;
}
}
return(0);
}
Snippet of strace for echo "hello" > ./a.txt
openat(AT_FDCWD, "./a.txt", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
fcntl64(1, F_GETFD) = 0
fcntl64(1, F_DUPFD, 10) = 10
fcntl64(1, F_GETFD) = 0
fcntl64(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "hello\n", 6) = 6
dup2(10, 1) = 1
fcntl64(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0

observe physical address content

I compile and run the pagemap_dump.c from here :
/proc/[pid]/pagemaps and /proc/[pid]/maps | linux
and another application attach a exist shared memory and every time I run pagemap_dump , the shared memory's physical address is really the same (virtual memory address is different each time I observe) :
./pagemap_dump.exe 2135 <== 2135 is pid
./pagemap_dump.exe 5864 <== 5864 is pid
./pagemap_dump.exe 5110 <== 5110 is pid
the output :
7fabb37e1000 54edde 0 1 0 1 /dev/shm/memtest.shared
7f0040fa1000 54edde 0 1 0 1 /dev/shm/memtest.shared
7ffbd2869000 54edde 0 1 0 1 /dev/shm/memtest.shared
these 3 application attach memtest.shared and their physical address are all 0x54edde in my linux ,
then I try to access the contents from /dev/mem in this
physical address :
How to access physical addresses from user space in Linux?
int main(int argc, char** argv)
{
if (argc < 3) {
printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
return 0;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
// Truncate offset to a multiple of the page size, or mmap will fail.
size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (offset / pagesize) * pagesize;
off_t page_offset = offset - page_base;
int fd = open("/dev/mem", O_RDONLY|O_SYNC);
unsigned char *mem = (unsigned char *) mmap(NULL, page_offset + len, PROT_READ , MAP_SHARED, fd, page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return -1;
}
size_t i;
for (i = 0; i < len; ++i)
printf("%02x ", (int)mem[page_offset + i]);
return 0;
} // main
chown root testz1.exe
chmod 4775 testz1.exe
./testz1.exe 5565918 16 <=== 0x54edde = 5565918
I got Can't map memory: Operation not permitted
./testz1.exe 54edde 16
I got some thing not like "hello world" what I strcpy to
/dev/shm/memtest.shared when I create it .
Since I seems to get the physical address of /dev/shm/memtest.shared , how can I get the right way to get the contents from /dev/mem of this physical address ?!
Edit :
After download devmem2.c and run :
./devmem2.exe 0x54edde w
get the following message :
/dev/mem opened.
Error at line 75, file devmem2.c (1) [Operation not permitted]
Look like 0x54edde is not a physical address as I expect , but Why it happened?!
pagemap_dump.c really show the physical address of /dev/shm/memtest.shared
for all processes which attach it the value 0x54edde , then Why
devmem2.c can not access it right ?!

linux mmap access to PCI memory region from user space application

As a first level test of my PCI driver I hoped I could gain access to the pci_iomap region via the /sys/bus/pci/devices/0000:01:00.0/resource0
file from my user application. The man page for mmap, the sample Program I found, and other posts seem to indicate that user process access should work. But some articles seem to indicate that the mmap call needs to be done from within the kernel via an ioctl accessor.
My Question is should mmap() of the PCI sysfs resource file work from application space?
When I run my code mmap returns what looks like a valid address but I get a Bus error when I try and access the virtual address.
I believe my end device a PCI to Xilinx AXI bridge which is on the FPGA is functioning ok since I can R/W to it over a windows PCIe utilty (Win Driver)
I am on an NXP LS1021A ARM7 processor with Linux ver 3.12.37.
Thanks
Bill
Not that I want anyone to debug my code but what I am doing may be best explained by the code so I have included it also. I apologies if the pasted code doesn't display correctly. Hopefully it does.
I run the code below and get
root#ls1021aiot:~# pcimem /sys/bus/pci/devices/0000:01:00.0/resource0 0 w
/sys/bus/pci/devices/0000:01:00.0/resource0 opened.
Target offset is 0x0, page size is 4096 map mask is 0xFFF
mmap(0, 4096, 0x3, 0x1, 3, 0x0)
mmap(0, 4096, 0x3, 0x1, 3, 0x0)
PCI Memory mapped 4096 byte region to map_base 0x76fb5000.
PCI Memory mapped access 0x 76FB5000.
Bus error
/*
* pcimem.c: Simple program to read/write from/to a pci device from userspace.
*
* Copyright (C) 2010, Bill Farrow (bfarrow#beyondelectronics.us)
*
* Based on the devmem2.c code
* Copyright (C) 2000, Jan-Derk Bakker (J.D.Bakker#its.tudelft.nl)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <linux/pci.h>
#define PRINT_ERROR \
do { \
fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); \
} while(0)
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char **argv) {
int fd;
void *map_base, *virt_addr;
uint32_t read_result, writeval;
char *filename;
off_t target;
int access_type = 'w';
if(argc < 3) {
// pcimem /sys/bus/pci/devices/0001\:00\:07.0/resource0 0x100 w 0x00
// argv[0] [1] [2] [3] [4]
fprintf(stderr, "\nUsage:\t%s { sys file } { offset } [ type [ data ] ]\n"
"\tsys file: sysfs file for the pci resource to act on\n"
"\toffset : offset into pci memory region to act upon\n"
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n"
"\tdata : data to be written\n\n",
argv[0]);
exit(1);
}
filename = argv[1];
target = strtoul(argv[2], 0, 0);
if(argc > 3)
access_type = tolower(argv[3][0]);
if((fd = open(filename, O_RDWR | O_SYNC)) == -1){
PRINT_ERROR;
}
printf("%s opened.\n", filename);
printf("Target offset is 0x%x, page size is %ld map mask is 0x%lX\n", (int) target, sysconf(_SC_PAGE_SIZE), MAP_MASK);
fflush(stdout);
/* Map one page */
#if 0
//map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) (target & ~MAP_MASK));
//map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
#endif
printf("mmap(%d, %ld, 0x%x, 0x%x, %d, 0x%x)\n", 0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (int) (target & ~MAP_MASK));
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (target & ~MAP_MASK));
if(map_base == (void *) -1){
printf("PCI Memory mapped ERROR.\n");
PRINT_ERROR;
close(fd);
return 1;
}
printf("mmap(%d, %ld, 0x%x, 0x%x, %d, 0x%x)\n", 0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (int) (target & ~MAP_MASK));
printf("PCI Memory mapped %ld byte region to map_base 0x%08lx.\n", MAP_SIZE, (unsigned long) map_base);
fflush(stdout);
virt_addr = map_base + (target & MAP_MASK);
printf("PCI Memory mapped access 0x %08X.\n", (uint32_t ) virt_addr);
switch(access_type) {
case 'b':
read_result = *((uint8_t *) virt_addr);
break;
case 'h':
read_result = *((uint16_t *) virt_addr);
break;
case 'w':
read_result = *((uint32_t *) virt_addr);
printf("READ Value at offset 0x%X (%p): 0x%X\n", (int) target, virt_addr, read_result);
break;
default:
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
exit(2);
}
fflush(stdout);
if(argc > 4) {
writeval = strtoul(argv[4], 0, 0);
switch(access_type) {
case 'b':
*((uint8_t *) virt_addr) = writeval;
read_result = *((uint8_t *) virt_addr);
break;
case 'h':
*((uint16_t *) virt_addr) = writeval;
read_result = *((uint16_t *) virt_addr);
break;
case 'w':
*((uint32_t *) virt_addr) = writeval;
read_result = *((uint32_t *) virt_addr);
break;
}
printf("Written 0x%X; readback 0x%X\n", writeval, read_result);
fflush(stdout);
}
if(munmap(map_base, MAP_SIZE) == -1) { PRINT_ERROR;}
close(fd);
return 0;
}
You can take a look at pci_debug program code, available here. If you have bus error, it's maybe a FPGA design problem. Is your IP AXI bus accept 32bits/16bits/8bits access ?
Be sure to access memory with right address (if 32bits address must divisible by 4, if 16 bits by 2).
Still haven't found a fix for this on the NXP LS1021A processor, But I tested on a x86_64 machine and there the mmap worked to the same PCIe Edge target. The NXP ARMv7 processor I have is a 40 bit internal address space device but is a 32 bit processor. This must present a special case to the Virtual address tables which is not handled correct. The 2 PCIe root devices are mapped into the higher address space. I ended up just writing an IOCTL driver and used the read and write routines in kernel space to acccess the pci_iomap() pointer which I cached when the device is probed. NXP offered a suggestion which I quote below, but I still got the same Bus Error. If they ever solve it I will update.
" Regarding the mmap. Here is a response from the software team.
use mmap64() to map 40bit phy_address to a 32 virt_addr or continue using mmap but add “-D_FILE_OFFSET_BITS=64” when compiling."
UPDATE.
I received a new version of the toolchain from NXP and mmap64 was available in it. My take on this is that mmap64 support need only be used on 32bit systems when you want to access files and offsets larger > 4GB files. By replacing the calls to mmap with mmap64 the tests I used now run correctly.
I had the same issue (bus error), and nothing I did worked.
Eventually, It was solved by using uio_pci_generic.
The driver seems to allow using mmap from userspace (without a driver, this can't be done).
More information can be found here:
https://github.com/rumpkernel/wiki/wiki/Howto:-Accessing-PCI-devices-from-userspace
In case someone else lands here, note that you also want to look at the output of lspci or lshw for the resource of interest. Not all the resources are writable. I have certainly seen bus errors, but do have things working for my purpose. Have not typified all cases.

How does limits on the shared memory work on Linux

I was looking into the Linux kernel limits on the shared memory
/proc/sys/kernel/shmall
specifies the maximum amount of pages that can be allocated. Considering this number as x and the page size as p. I assume that "x * p" bytes is the limit on the system wide shared memory.
Now I wrote a small program to create a shared memory segment and i attached to that shared memory segment twice as below
shm_id = shmget(IPC_PRIVATE, 4*sizeof(int), IPC_CREAT | 0666);
if (shm_id < 0) {
printf("shmget error\n");
exit(1);
}
printf("\n The shared memory created is %d",shm_id);
ptr = shmat(shm_id,NULL,0);
ptr_info = shmat(shm_id,NULL,0);
In the above program ptr and ptr_info were different. So the shared memory is mapped to 2 virtual addresses in my process address space.
When I do an ipcs it looks like this
...
0x00000000 1638416 sun 666 16000000 2
...
Now coming to the shmall limit x * p noted above in my question. Is this limit applicable on the sum of all the virtual memory allocated for every shared memory segment? or does this limit apply on the physical memory?
Physical memory is only one here (shared memory) and from the program above when I do 2 shmat's there is twice the amount of memory allocated in my process address space. So this limit will hit soon if do continuous shmat's on a single shared memory segment?
The limit only applies to physical memory, that is the real shared memory allocated for all segments, because shmat() just maps that allocated segment into process address space.
You can trace it in the kernel, there is only one place where this limit is checked — in the newseg() function that allocates new segments (ns->shm_ctlall comparison). shmat() implementation is busy with a lot of stuff, but doesn't care at all about shmall limit, so you can map one segment as many times as you want to (well, address space is also limited, but in practice you rarely care about this limit).
You can also try some test from userspace with a simple program like this one:
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
unsigned long int get_shmall() {
FILE *f = NULL;
char buf[512];
unsigned long int value = 0;
if ((f = fopen("/proc/sys/kernel/shmall", "r")) != NULL) {
if (fgets(buf, sizeof(buf), f) != NULL)
value = strtoul(buf, NULL, 10); // no proper checks
fclose(f); // no return value check
}
return value;
}
int set_shmall(unsigned long int value) {
FILE *f = NULL;
char buf[512];
int retval = 0;
if ((f = fopen("/proc/sys/kernel/shmall", "w")) != NULL) {
if (snprintf(buf, sizeof(buf), "%lu\n", value) >= sizeof(buf) ||
fwrite(buf, 1, strlen(buf), f) != strlen(buf))
retval = -1;
fclose(f); // fingers crossed
} else
retval = -1;
return retval;
}
int main()
{
int shm_id1 = -1, shm_id2 = -1;
unsigned long int shmall = 0, shmused, newshmall;
void *ptr1, *ptr2;
struct shm_info shminf;
if ((shmall = get_shmall()) == 0) {
printf("can't get shmall\n");
goto out;
}
printf("original shmall: %lu pages\n", shmall);
if (shmctl(0, SHM_INFO, (struct shmid_ds *)&shminf) < 0) {
printf("can't get SHM_INFO\n");
goto out;
}
shmused = shminf.shm_tot * getpagesize();
printf("shmused: %lu pages (%lu bytes)\n", shminf.shm_tot, shmused);
newshmall = shminf.shm_tot + 1;
if (set_shmall(newshmall) != 0) {
printf("can't set shmall\n");
goto out;
}
if (get_shmall() != newshmall) {
printf("something went wrong with shmall setting\n");
goto out;
}
printf("new shmall: %lu pages (%lu bytes)\n", newshmall, newshmall * getpagesize());
printf("shmget() for %u bytes: ", (unsigned int) getpagesize());
shm_id1 = shmget(IPC_PRIVATE, (size_t)getpagesize(), IPC_CREAT | 0666);
if (shm_id1 < 0) {
printf("failed: %s\n", strerror(errno));
goto out;
}
printf("ok\nshmat 1: ");
ptr1 = shmat(shm_id1, NULL, 0);
if (ptr1 == 0) {
printf("failed\n");
goto out;
}
printf("ok\nshmat 2: ");
ptr2 = shmat(shm_id1, NULL, 0);
if (ptr2 == 0) {
printf("failed\n");
goto out;
}
printf("ok\n");
if (ptr1 == ptr2) {
printf("ptr1 and ptr2 are the same with shm_id1\n");
goto out;
}
printf("shmget() for %u bytes: ", (unsigned int) getpagesize());
shm_id2 = shmget(IPC_PRIVATE, (size_t)getpagesize(), IPC_CREAT | 0666);
if (shm_id2 < 0)
printf("failed: %s\n", strerror(errno));
else
printf("ok, although it's wrong\n");
out:
if (shmall != 0 && set_shmall(shmall) != 0)
printf("failed to restrore shmall\n");
if (shm_id1 >= 0 && shmctl(shm_id1, IPC_RMID, NULL) < 0)
printf("failed to remove shm_id1\n");
if (shm_id2 >= 0 && shmctl(shm_id2, IPC_RMID, NULL) < 0)
printf("failed to remove shm_id2\n");
return 0;
}
What is does is it sets the shmall limit just one page above what is currently used by the system, then tries to get page-sized new segment and map it twice (all successfully), then tries to get one more page-sized segment and fails to do that (execute the program as superuser because it writes to /proc/sys/kernel/shmall):
$ sudo ./a.out
original shmall: 18446744073708503040 pages
shmused: 21053 pages (86233088 bytes)
new shmall: 21054 pages (86237184 bytes)
shmget() for 4096 bytes: ok
shmat 1: ok
shmat 2: ok
shmget() for 4096 bytes: failed: No space left on device
I did not find any Physical memory allocation at do_shmat function (linux/ipc/shm.c)
https://github.com/torvalds/linux/blob/5469dc270cd44c451590d40c031e6a71c1f637e8/ipc/shm.c
so shmat consumes only vm (your process address space),
the main function of shmat is mmap

Resources