Consider a file, that is edited with a frequency of tens, hundreds multiple processes per second. Since two or more processes can race for a file access for writing, there need a mechanism to be implemented, to make only one process to access file at one time.
As I understand, calling fopen (or open), until the fclose will do the job - these functions guarantee, that only one process will access the file.
The problem is that the file is needed to be truncated after being opened, because there is a need to read it first and after that rewrite. If there will be two fopen calls, obviously it will not guarantee cross-process safety.
This answer recommends to use freopen function after fopen and before fclose calls, however, according to the Linux man-pages, freopen documentation:
If pathname is not a null pointer, freopen() shall close any file descriptor associated with stream.
Currently, the only solution I see is to create an associated file, with the needed to be accessed, and lock it, instead of locking the needed file (although it also be locked for sure), for the period, needed file will be read, closed, truncated, wrote, closed.
Is at least such solution will guarantee safety? Any better solutions?
As users in comments explained me, fopen does not prevent a file from being opened in another process at all. Instead, flock should be called additionally.
So the question is can I lock the file, read, truncate, write and then unlock it, or I should use solution with associated file, I described above?
Specific summary question
- As I understand,
flockaccepts file descriptor need to be locked as an argument (and exactly descriptor fromopen, notfopen). - But, as I understand, read, truncation and write file requires opening, closing and again opening the file.
- So, according to second fact, if I lock the file before I call
close, will it be locked afterclosecall? If it automatically unlocks due tocloseit does not make any sense. If it is not unlocks, how to unlock it after all manipulations?
You write,
and you clarify that
That is, you want to serialize units of read(+truncate)+write access to the file.
Absolutely not. Neither C nor POSIX makes any such guarantee. In practice, it is not typically true on POSIX systems, and it is not true on Linux in particular. You might see such behavior on Windows, however, which could be where you got the idea.
If you must use a physical file for this purpose then you will need to perform some sort of locking around access to it. Provided that the file is on a local filesystem, the most natural locking mechanism would be
flock(). This implements an advisory locking system, meaning that it applies only to processes that explicitly gate their access viaflock(), but that does not appear to be an issue for your planned use.flock()has the advantage that if a process dies while holding the lock (with the result that the underlying open file description is closed) then the lock is automatically released.flock()has the disadvantage that it will not work if the file is replaced -- only if processes always modify it in place.Another alternative would be a process-shared mutex or semaphore or a named semaphore, which the processes would use (again, cooperatively) to ensure the needed serialization. This has the advantage that it could still work if the file is altogether replaced instead of modified in-place, but the possible disadvantage that if one of the participating processes dies while holding the mutex / semaphore locked then the whole system gets jammed. (But if you use System V semaphores instead of POSIX semaphores then that shortcoming can be addressed.)
But what if you don't use a physical file? What you're describing sounds like it will require moderately high I/O bandwidth, and it will do a great deal of rewriting the same sectors of the underlying physical medium. Why do you want to hit a disk so hard? Or to deal with slow file I/O? You probably would be better off using shared memory (still protected by mutex or semaphore). Or you could implement a data broker service that runs on the system and mediates access to the data.
Addendum - response to additional questions about
flockSort of.
flock()requires a file descriptor (fromopen) to identify the file to lock. The file is locked, not the file descriptor, but that lock is associated with the file descriptor.And if you want to use
stdiofunctions to read and write the file, then you can get aFILE *from the file descriptor viafdopen(). The file descriptor is still underneath, so that will not interfere with the locking. If you do this, however, then you should be sure to close viafclose()ing the stream fromfdopen, not directlyclose()ing the file descriptor underneath.That is incorrect.
If the data to be written are the same length then you don't need to explicitly truncate at all. After reading the file,
rewind()ing to the beginning orfseek()ing orlseek()ing there, then writing, then closing will have the desired effect.If the data to be written might be shorter then you can
truncate()the file to any length (including 0) without first closing it.No, and it doesn't need to be (see above). Moreover, it is a convenience that you just need to close the file, not explicitly unlock it.
Outline
Note that for clarity and simplicity, I have omitted testing for or handling errors from the various functions. Production code does not have that luxury.