I am experimenting with mmap and came with the following sample code:
int main() {
int fd;
char *filename = "/home/manu/file";
struct stat statbuf;
int i = 0;
char c = *(filename);
// Get file descriptor and file length
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("fopen error");
}
if (fstat(fd, &statbuf) < 0) {
perror("fstat error");
}
printf("File size is %ld\n", statbuf.st_size);
// Map the file
char* mmapA = (char*) mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE,
fd, 0);
if (mmapA == MAP_FAILED) {
perror("mmap error");
return 1;
}
// Touch all the mapped pages
while (i < statbuf.st_size) {
c = mmapA[i];
i++;
}
c++;
// Close file descriptor
if (close(fd) == -1) {
perror("close");
return 1;
}
//Unmap file
munmap(mmapA, statbuf.st_size);
return EXIT_SUCCESS;
}
The file size is 137948 bytes = 134,7 kilobytes.
To inspect program’s memory I am using top, mainly the RES and VIRT columns. I am looking for these values at three different places:
- just before the
mmapcall - just after the
mmapcall - after having read all the mapped memory to have the file’s effectively loaded into main memory (after page faults)
The value reported by top are
- VIRT = 1828 RES = 244
- VIRT = 1964 RES = 248
- VIRT = 1964 RES = 508
1964 – 1828 = 136, I guess in kilobytes and thus perfectly match the file’s size.
But I can’t understand the RES difference of 508 – 248 = 260 .. Why is it different from virtual memory size and file size ?
One thing is certain: the results depend on the state of the system and not only on the running application. On my machine, the increase in RES was 136 kB the first two times I run the program, but the subsequent runs didn’t involve any increase at all – probably the OS already had the whole file in cache. Interestingly, the values themeselves differed significantly between runs. In the first run the jump in RES was from 344 to 480 kB, but the latter runs had a RES value of 348 kB all the time. There was a similar change in SHR: a jump of 136 kB first time and no change later.
I was able to force the original case (with the 136 kB jump) at will by overwriting the file which is later mapped with with zeros using
ddbefore running the app.I looked at
pmapsoutput but it was exactly the same in both cases and didn’t change after the call tommap().I can’t reproduce the oversized RES jump here, but here’s what you can do. Suppose your binary is compiled as
a.out. Insert a 10 second sleep right after themmap()and another 10 second sleep just beforemunmap(). This gives a time window to dump interesting information. We will read from/procwhich exactly files are resident in memory. In order to do this, open up two tabs in your terminal, in one runand then immediately in the other tab:
This will create 4 snapshots of the program’s map states in four separate files. The difference between one of the consecutively numbered snapshot should show what changes during the surge in RES size. On my machine during a sample run, the difference was between snapshots 1 and 2, and the change was [note I changed the name of mapped file but it’s not important here]:
What happens is exactly what should: the mapped file is initially not resident at all and 136 kB are resident later on.
On your system, the diff should lead you to the source of the additional change in RES – you should be able to find out the name of the other file(s) whose
Rssvalue changes. Some entries are not files, but other memory areas, for example you may find markers such as[heap]and[stack]. This should also prove or disprove nos’ suggestion about system libraries being loaded and stack usage growing.