Monday, March 28, 2011

clear buffer cache on Mac OS X

Is there a way to programatically clear the buffer cache on the Mac, preferrably in C?

Basically, I'm looking for the source of 10.5's purge command. EDIT: I now see this is part of the CHUD tools, for which it seems the source isn't directly available. However, I'm still looking for some code to do the same.

From stackoverflow
  • You could use sync(2) several times (as in the well-known idiom sync; sync; sync). I can't seem to find the purge source code but it may just be part of the man packages available in 10.5.6 code

    Jason Coco : purge is part of CHUD, so that's why you can't find the source ;-)
    Ben Alpert : So, does that mean the source is unavailable, or just hiding?
    Ben Alpert : Keltia, that will only force writes to be written to disk; it doesn't actually clear the buffer cache.
    Keltia : Apart from trying to disassemble it or run it under DTrace, I do not see a way short of gaining access to the source code then.
  • When you don't have the source code for the tool you wish to emulate (as is the case here), there are a number of ways to go about it.

    1/ From your C code, simply call the tool with a system() function call. This works well as long as there's no visible effect (such as opening a graphical window). You could use system("/path/to/purge -purgargs >/dev/null 2>&1");, for example.

    2/ Reverse-engineer the code to see how it's actually doing it. This is somewhat trickier since it will require knowledge of the assembler language, system calls and many other things.

    3/ Contact the developers to obtain tips on how it was done. This doesn't have to be a "send me the code so I can rip it off and make money" question. You could phrase it as "I have an interest in using purge for development but I'm unsure exactly what is does" or "I have security issues with running the code, the powers that be won't let me run it unless we know exactly what it does". Then you code yours to do the same.

    Me, I would just use option 1 if possible (I'm inherently lazy :-). If you're going to write a tool to compete with purge (and this'll be hard given it's free), option 2 is probably the best bet.

    Ben Alpert : I'm guessing that Apple wouldn't be that helpful if I went and asked for some of their proprietary source code.
    paxdiablo : No, I don't think they would be either, which is why I said to phrase it more subtly :-) Anyway, you don't need their code (and using it would possibly make your code a derivative work/copyright violation). You only need to know how to do it conceptually.
  • Wouldn't you be interested in turning off the cache for a file instead? Depending on what you are trying to achieve, it could be an alternative. Good summary here.

    UBC can be cleared by running 'purge' which allocates a lot of memory to force the cache to clear.

    fcntl(fd, F_GLOBAL_NOCACHE, 1)
    

    can be used turn caching off for a particular file. This can be done in any process and the file can be closed after.

    lpfavreau : I'm just giving that as an alternative route, not knowing what you're doing exactly. Just ignore that if it doesn't fit the bill. It's just that _sometimes_, when you need to purge a cache often, it might be because it shouldn't be cached in the first place.
  • It seems that:

    You can use usr/bin/purge (type purge in the terminal) to flush the disk cache (inactive memory), or you can do many random reads from the hard disk to do the same thing.

    Taken from a comment from user guns.

  • I've disassembled the function in question (_utilPurgeDiskBuffers) from the CHUD framework. The function doesn't seem to be very complex, but since I'm no MacOS programmer, the imports and called sys APIs don't make much sense to me.

    The first thing the API does is to call another function, namely _miscUtilsUserClientConnect_internal. This function seems to establish a connection to the CHUD kernel extension.
    To do this, it calls _getCHUDUtilsKextService which tries to locate the CHUD kernel extension by enumerating all kexts using the IORegistryCreateIterator imported from the I/O kit. After the kext has been found, it is opened via _IOServiceOpen.

    At this point we have a connection to the CHUD kext (at least that's my understanding from the disassembly listing).

    Finally a call to IOConnectMethodStructureIStructureO is made, which I guess carries out the real magic.
    Without knowing some internal details or the signature of this function the parameters don't make sense to me.

    Here's the disassembly, though:

    __text:4B0157A7 lea     eax, [ebp+var_1C]
    __text:4B0157AA mov     dword ptr [esp+14h], 0
    __text:4B0157B2 mov     [esp+10h], eax
    __text:4B0157B6 mov     [esp+0Ch], eax
    __text:4B0157BA mov     dword ptr [esp+8], 0
    __text:4B0157C2 mov     dword ptr [esp+4], 0Eh
    __text:4B0157CA mov     [esp], edx
    __text:4B0157CD call    _IOConnectMethodStructureIStr
    

    Note that var_1C has been zeroed out before.

    Hopefully some of you can make more sense out of those syscalls. If you want more information, let me know.

    Update:
    To get you started, just take the AppleSamplePCIClient.c example from the IO kit SDK. This does basically what the purge application from the CHUD tools does.
    The only thing you would have to change are the parameters to the final _IOConnectMethodStructureIStr call. Take them from the disassembly listing above. I cannot test all this stuff since I don't have a Mac.

0 comments:

Post a Comment