30

I know I can drop everything from the Linux filesystem cache, but is there a way to drop just one specific file? Or prevent a file from being cached? Or tell a process not to cache any files it writes?

I have a process that reads a lot of little files and writes a big file. I want to keep the little files in cache to avoid disk seeks, and I don't care about caching the big file.

1
  • 2
    Regarding the bounty, I am specifically interested in the title question: dropping a specific file from the cache (as opposed to preventing it from getting there in the first place). Oct 17, 2014 at 22:45

5 Answers 5

28
+100

Potential Method #1 - F_DROP_CACHES

I found a method from 2012 that discusses a proposed patch to the Linux kernel in this mail thread titled: Re: [RFC Patch] fs: implement per-file drop caches.

excerpt

Cong> This is a draft patch of implementing per-file drop caches.

Interesting. So can I do this from outside a process? I'm a SysAdmin, so my POV is from noticing, finding and fixing performance problems when the system is under pressure.

Cong> It introduces a new fcntl command  F_DROP_CACHES to drop  
Cong> file caches of a specific file. The reason is that currently  
Cong> we only have a system-wide drop caches interface, it could  
Cong> cause system-wide performance down if we drop all page caches  
Cong> when we actually want to drop the caches of some huge file.

How can I tell how much cache is used by a file? And what is the performance impact of this when run on a busy system? And what does this patch buy us since I figure the VM should already be dropping caches once the system comes under mem pressure...

Cong> Below is small test case for this patch:

The thread includes both a testcase and the actual patch to several files within the Linux kernel which adds an additional function to fs/drop_caches.c called drop_pagecache_file(struct file *filp). This function is then accessible through the frontend tool, fnctl.c via the command F_DROP_CACHES. This case calls this function:

file_drop_caches(filp, arg);

Which handles the dropping of all the caches associated with the given file. From the file include/linux/mm.h:

void file_drop_caches(struct file *filp, unsigned long which);
So this can be used?

I found no evidence that this patch ever made its way into the main Linux kernel code repository, so this option would appear to be available, only if you're willing to recompile the Linux kernel yourself.

Potential Method #2 - Using dd

In that same thread, another user mentions a completely different methodology that makes use of dd.

The following is excerpt from that email

This is useful functionality. Though isn't it already provided with POSIX_FADV_DONTNEED? This functionality was added to GNU dd (8.11) a year ago.

Here are the examples from that patch:
  • Advise to drop cache for whole file

     $ dd if=ifile iflag=nocache count=0
    
  • Ensure drop cache for the whole file

     $ dd of=ofile oflag=nocache conv=notrunc,fdatasync count=0
    
  • Drop cache for part of file

     $ dd if=ifile iflag=nocache skip=10 count=10 of=/dev/null
    
  • Stream data using just the read-ahead cache

     $ dd if=ifile of=ofile iflag=nocache oflag=nocache
    
Testing it out

I wasn't 100% positive how to test this out but I came up with the following approach.

  1. make a 100MB file

    $ dd if=/dev/urandom of=sample.txt bs=100M count=1
    
  2. trace file accesses using fatrace

    $ sudo fatrace | grep sample.txt
    
  3. run top so we can monitor memory usage, note amount free.

    $ top
    
  4. open file, note amount of free memory now. Note the fatrace of the file sample.txt.

    $ cat sample.txt > /dev/null
    
  5. drop the file from memory, note amount of free memory now. Note the output of fatrace.

    $ sudo dd of=/home/saml/tst/162600/sample.txt \
        oflag=nocache conv=notrunc,fdatasync count=0
    

Example

In terminal #1:
$ dd if=/dev/urandom of=sample.txt bs=100M count=1
1+0 records in
1+0 records out
104857600 bytes (105 MB) copied, 7.37996 s, 14.2 MB/s

$ ls -l sample.txt 
-rw-rw-r--. 1 saml saml 104857600 Oct 17 22:54 sample.txt
In terminal #2:
$ top
...
KiB Mem:   7968336 total,  6900956 used,  1067380 free,   267080 buffers
...
In terminal #3:
$ sudo fatrace | grep sample.txt
Now open the file, sample.txt, and note the amount of RAM. In terminal #1.
$ cat sample.txt > /dev/null
In terminal #2:
KiB Mem:   7968336 total,  7011896 used,   956440 free,   267336 buffers
Notice the output of fatrace in terminal #3:
cat(25940): R /home/saml/tst/162600/sample.txt
cat(25940): R /home/saml/tst/162600/sample.txt
cat(25940): RC /home/saml/tst/162600/sample.txt
Now remove the file from RAM, in terminal #4:
$ sudo dd of=/home/saml/tst/162600/sample.txt \
    oflag=nocache conv=notrunc,fdatasync count=0
Note the output of fatrace in terminal #2:
dd(26229): O /home/saml/tst/162600/sample.txt
dd(26229): CW /home/saml/tst/162600/sample.txt
Note the RAM in terminal #3:
KiB Mem:   7968336 total,  6908364 used,  1059972 free,   267364 buffers

So it would seem that all of the that was consumed by the file in RAM is freed.

Potential Method #3 - python-fadvise

Thanks to a comment by @frostchutz, there's another tool, a Python script, named [pyadvise][4] which provides a much simpler interface than the above dd methods. This script makes use of the same posix_fadvise(2) interface.

Example
$ sudo pyadvise --help
Usage: 
    pyadvise [options] [FILE]..

Options:
  -h, --help        show this help message and exit
  -w, --willneed    The specified files will be accessed in the near future
  -s, --sequential  The application expects to access the specified files
                    sequentially (with lower offsets read before higher ones)
  -d, --dontneed    The specified files will not be accessed in the near
                    future
  -r, --random      The specified files will be accessed in random order
  -o, --noreuse     The specified files will be accessed only once. Under
                    Linux, this operation is a no-op; see contrib/copyfileobj-
                    fadvise.py in the python-fadvise source tree for an
                    example on how to achieve approximately the same effect
  -n, --normal      Indicates that the application has no advice to give about
                    its access pattern for the specified files. If no advice
                    is given for an open file, this is the default assumption
  -v, --verbose     Explain what is being done

And if we repeat the above test and use pyadvise in place of dd:

$ pyadvise -d /home/saml/tst/162600/sample.txt

I noticed an identical drop in the RAM being consumed as before when I used dd.

9
  • dd works for me. I ended up with chris-lamb.co.uk/projects/python-fadvise myself which is the same thing in a more obvious command. Oct 18, 2014 at 2:37
  • @frostschutz - very cool. I hadn't heard of this until Gilles asked if anyone knew how to do this in chat. python-fadvise is much easier, I've added an example showing dd.
    – slm
    Oct 18, 2014 at 3:50
  • The link for the python script should be moved into the main body of the question. Comments can disappear without trace. A edit will at worst still remain in history. Having said, that, a Google search finds it easily, so not a big deal. Oct 18, 2014 at 8:05
  • It even seems to work without sudo, so anyone who can see a file (even without write permission) can have its cache dropped, that's... interesting. Oct 18, 2014 at 12:46
  • 2
    There is os.posix_fadvise() in Python's standard libray now. Aug 31, 2017 at 6:02
4

Expanding @geekosaur's answer you can force the use of O_DIRECT by using LD_PRELOAD and the program here: http://arighi.blogspot.com/2007/04/how-to-bypass-buffer-cache-in-linux.html

That code forces O_DIRECT for all files. However, simply adding some more strncmp logic in __do_wrap_open you can selectively apply O_DIRECT.

Disclaimer: I have not tested this.

3

You can open individual files with the O_DIRECT flag (see man 2 open) — read the NOTES section of that manpage carefully, and consider whether you also want/need O_SYNC.

2
  • 1
    Well, my process is cat, and I'd rather not rewrite it. :) I was hoping for a command-line tool or a /proc/sys knob.
    – Jay Hacker
    Apr 19, 2012 at 22:21
  • 2
    Worse than that, I suspect you really mean you're using redirection, so your process is the shell. I don't know of a per-file way to control this aside from the open flag; you would indeed need to write a program to do it. (cat -u only disables stdio buffering, not OS buffering.)
    – geekosaur
    Apr 19, 2012 at 22:36
0

The only way to prevent a file from being cached is to use O_DIRECT. dd is your friend.
If you don't want to use O_DIRECT, notice that the damage might be done already afterwards (other more important things might be discarded from the cache).

# fgrep MemFree: /proc/meminfo
4691788 KB
# dd if=massivefile of=/dev/null bs=2M  
# fadv <massivefile
# fgrep MemFree: /proc/meminfo
5097264 KB

where fadv.c is (use stdin to make the code ultra simple):

#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{ posix_fadvise(0, 0, lseek(0, 0, SEEK_END), POSIX_FADV_DONTNEED);
  return 0;
}

Notice that afterwards there is more free memory than before, meaning data that was in the cache before was thrown out. However if you read the file with O_DIRECT:

  # fgrep MemFree: /proc/meminfo
  MemFree:         5091464 kB
  # dd if=verylargefile bs=2M of=/dev/null iflag=direct
  6141+1 records in
  6141+1 records out
  12880669515 bytes (13 GB) copied, 72,7641 s, 177 MB/s
  # fgrep MemFree: /proc/meminfo
  MemFree:         5074164 kB

Now there's a little less memory free, meaning other activity used up some memory, but the actual read of this 13GB file didn't wash the cache.

-1

If you want to force a file to always use O_SYNC you can mark it as so in the extended attributes with chattr +S $file:

man chattr:

When a file with the 'S' attribute set is modified, the changes are written synchronously on the disk; this is equivalent to the 'sync' mount option applied to a subset of the files.

O_SYNC forces the data+metadata to be written to the disk buffers, but it still passes through the page cache. O_DIRECT bypasses the page cache.

But be aware that opening it with O_DIRECT would be detrimental to performance, if the big file it's just being appended the difference might be small. But if the big file it's been rewritten at random places O_DIRECT will be a very big hit on performance, even taking into account that it having it in cache could possibly evict from cache some of the small read files.

If you have the ram to keep all the small files there, you could approach the problem the other way. Make sure that the small files are always in ram, then I would suggest to copy them to tmpfs:

tmpfs puts everything into the kernel internal caches and grows and shrinks to accommodate the files it contains

5
  • 1
    chattr +S is not the same thing as O_DIRECT, it's the same thing as O_SYNC. O_DIRECT causes reads not to be cached (which is what this question is about), and writes not to be buffered, with no guarantee. O_SYNC causes only writes not to be buffered. Oct 19, 2014 at 20:15
  • @Gilles you are right, I read the question and thought about flushing data to the disk as I had done it before. And there is another subtle, but important in this case, difference between O_DIRECT and O_SYNC, O_DIRECT bypasses the page cache, but O_SYNC not, it forces the data (and metadata) to be flushed to the disk, but it passes through page cache and is kept there to speedup reads. Should I change O_DIRECT for O_SYNC in my answer as not to let it stay with a wrong affirmation? Oct 20, 2014 at 9:58
  • This question asks about keeping a big file that it's been written out of the cache. I think opening it with O_DIRECT would be detrimental to performance, and if the big file it's just being appended the difference might be small. But if the big file it's been rewritten at random places O_DIRECT will be a very big hit on performance, even taking into account that it could possibly evict from cache some of the small read files. Oct 20, 2014 at 10:13
  • Changing O_DIRECT to O_SYNC would make your answer internally consistent, but still wrong considering the question. Oct 21, 2014 at 16:57
  • O_SYNC only forces write through, but make no difference for reads and will make zero difference on the cache, as the data that was read/written will stay. Nov 15, 2020 at 4:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .