How to use the same semaphore between program executions? - c

I have a simple program that only uses one process (each time it's executed), creates a semaphore with a key that is the file's name (ftok() function), and then writes a line to a file. The thing is, the semaphores (in this case, 2) have to do two things: one has to guarantee that no more than two programs write at the same time, and the other has to verify that only 10 lines maximum have been written to the file. So if I execute the program and the file already has 10 lines of text, it won't write anything to it.
This is my code:
#include "semaphores.h"
int main() {
int semaphoreLines = create_semaphore(ftok("Ex5.c", 0), 10);
int semaphoreWrite = create_semaphore(ftok("Ex5.c", 1), 1);
FILE *file;
int ret_val = down(semaphoreLines, 1);
if(ret_val != 0) {
printf("No more lines can be written to the file!\n");
exit(-1);
}
down(semaphoreWrite, 1);
file = fopen("Ex5.txt", "a");
fprintf(file, "This is process %d\n", getpid());
fclose(file);
up(semaphoreWrite, 1);
return 0;
}
When I execute it the first time, semaphoreLines goes to 9 (as intended), locks the semaphoreWrite to 0 (so no other process can write to the file), then writes and frees up the latter back to 1. The process terminates. I manually tell it to run again in Terminal. However, semaphoreLines should be 9 so when I down() it, it goes to 8 and so forth. The issue is, it gets back up at 10 again. I don't want this.
Maybe it's because I'm fairly new to semaphore programming, but I thought semaphores were public if they don't get created with 0 key. With the ftok(), I wanted it to be public so that if I run the program again it decrements it if possible and writes, if not it displays the error code and terminates. I mean, the semaphore doesn't get removed, so the second time the program gets executed it should see the semaphore value is 9, right...?
I don't really want to fork 10 processes and have them write one by one to the file in the same program...or is that the only way to do it?
P.S. The create_semaphore() function is part of my semaphores.h header file, which contains 4 simple functions I wrote so it's easier to use semaphores instead of running all that semget, semop, and semctl stuff every time I want to work with them.

The issue is, it gets back up at 10 again. I don't want this.
If you don't want this, then don't do it. You yourself are setting the semaphore value to 10 in create_semaphore(). Instead, pass IPC_EXCL in addition to IPC_CREAT to semget(), and if that yields errno EEXIST, just return from create_semaphore() and skip the semctl(SETVAL).

Related

read(...): Will read remember the change?

On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number.
This is the description I copy from man 2 read, I have a question to "the file position is advanced by this number" this statement.
What I want to do in my code is the following:
for (int i = 1; i < argc; i++) {
if (read(pipe_fd[i-1][0], &contribution, sizeof(int)) == -1) {
perror("reading from pipe from a child");
exit(1);
}
if (i == argc){
i = 1;
}
}
I am trying to repeatedly read data from the pipes that connect each child process to my parent process, my question to this post is: will read remember when it should continue reading when next time I call read again?
For example, suppose I am calling read(pipe_fd[1][0], &contribution, sizeof(int)), from what I understand here, I know read will read sizeof(int) bytes from the pipe, and somehow use fseek call or something like that to move sizeof(int) bytes to the next new starting position. But when I loop through, and change i back to read(pipe_fd[1][0]) again, will read remember its last starting position? (which is after the first read call, the new starting position I describe above) Or read will just assume nothing happens and read from the initial starting position instead of the new starting position?
Pipes don't have a file position, but read(2) will give you all the data written to the other end of the pipe, in the same order (first in, first out). When you call read() the next time on the same fd, you will get the data that follows what you got from the previous call on that fd. This is typically what you want, and you don't need to do anything special in between.

Chaos when fork() meets fopen()

I've found that a open filestream will get messed up if we do fork() before closing it. It is well known that concurrency, i.e., race conditions can happen when parent and child process want to modify the filestream. However, even when the child process doesn't ever touch the filestream, it still has undefined behavior. I was wondering if someone can explain this maybe from how the kernel deals with a filestream during the stages where child process is forked and exited.
Below is a quick snippet of a strange behavior:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
// Open file
FILE* fp = fopen("test.txt", "r");
int count = 0;
char* buffer = NULL;
size_t capacity = 0;
ssize_t line = 0;
while ( (line = getline(&buffer, &capacity, fp)) != -1 ) {
if (line > 0 && buffer[line - 1] == '\n') // remove the end '\n'
buffer[line - 1] = 0;
pid_t pid = fork();
if (pid == 0) {
// fclose(fp); // Magic line here: when you add this, everything is fine
if (*buffer == '2')
execlp("xyz", "xyz", NULL);
else
execlp("pwd", "pwd", NULL);
exit(1);
} else {
waitpid(pid, NULL, 0);
}
count++;
}
printf("Loops: %d\n", count);
return 0;
}
Just copy the code into a new file (e.g., test.c). And create a .txt file test.txt with the simple content
1
2
3
4
and run
$ gcc test.c && ./a.out
There are 4 lines in the file. The loops is expected to read each line and execute exactly 4 times (1 2 3 4). And I choose to let it exec an invalid command "xyz" when it's in the 2nd loop. Then, you will find the loop actually executes 6 times (1 2 3 4 3 4)! The fact is that, when all four commands executed are valid, nothing will go wrong. But if there is an invalid command executed, every command after it will be executed twice. (Please note that this strange behavior only occurs with Linux machine, my Mac OS is doing okay, not sure about Windows. So the problem is platform-dependent?)
It looks like whenever I fork(), the filestream in parent is no longer promised to be the old fp (non-deterministic behavior), even when my child process doesn't touch it.
A temporary solution I found is: fclose(fp) in child process. This will silence the above strange behavior, but in more complex conditions, there are still other things can be observed. It would be appreciated if somebody can give me some insight into this problem. Thanks
As said in the comments already you need to close open file descriptors before calling exec.
In this blogpost (section 4) there is a neat code sample you can use to ensure all fds are closed even in complex applications where you don't always know what files are open at the moment:
for ( i=getdtablesize(); i>2; --i)
close(i); /* close all descriptors */
(slightly modified to keep stdin, stdout, stderr open)
It's kind of hacky but it works. If you want to avoid that you can also set the O_CLOEXEC flag on each file descriptor that you open. Since when using fopen you do not directly call open() you can accomplish this by adding the 'e' flag to it (when using glibc >= 2.7):
FILE* fp = fopen("test.txt", "er");
When calling exec*() all file descriptors with this flag are automatically closed.

Why does forking my process cause the file to be read infinitely

I've boiled down my entire program to a short main that replicates the issue, so forgive me for it not making any sense.
input.txt is a text file that has a couple lines of text in it. This boiled down program should print those lines. However, if fork is called, the program enters an infinite loop where it prints the contents of the file over and over again.
As far as I understand fork, the way I use it in this snippet is essentially a no-op. It forks, the parent waits for the child before continuing, and the child is immediately killed.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
enum { MAX = 100 };
int main(){
freopen("input.txt", "r", stdin);
char s[MAX];
int i = 0;
char* ret = fgets(s, MAX, stdin);
while (ret != NULL) {
//Commenting out this region fixes the issue
int status;
pid_t pid = fork();
if (pid == 0) {
exit(0);
} else {
waitpid(pid, &status, 0);
}
//End region
printf("%s", s);
ret = fgets(s, MAX, stdin);
}
}
Edit: Further investigation has only made my issue stranger. If the file contains <4 blank lines or <3 lines of text, it does not break. However, if there are more than that, it loops infinitely.
Edit2: If the file contains numbers 3 lines of numbers it will infinitely loop, but if it contains 3 lines of words it will not.
I am surprised that there is a problem, but it does seem to be a problem on Linux (I tested on Ubuntu 16.04 LTS running in a VMWare Fusion VM on my Mac) — but it was not a problem on my Mac running macOS 10.13.4 (High Sierra), and I wouldn't expect it to be a problem on other variants of Unix either.
As I noted in a comment:
There's an open file description and an open file descriptor behind each stream. When the process forks, the child has its own set of open file descriptors (and file streams), but each file descriptor in the child shares the open file description with the parent. IF (and that's a big 'if') the child process closing the file descriptors first did the equivalent of lseek(fd, 0, SEEK_SET), then that would also position the file descriptor for the parent process, and that could lead to an infinite loop. However, I've never heard of a library that does that seek; there's no reason to do it.
See POSIX open() and fork() for more information about open file descriptors and open file descriptions.
The open file descriptors are private to a process; the open file descriptions are shared by all copies of the file descriptor created by an initial 'open file' operation. One of the key properties of the open file description is the current seek position. That means that a child process can change the current seek position for a parent — because it is in the shared open file description.
neof97.c
I used the following code — a mildly adapted version of the original that compiles cleanly with rigorous compilation options:
#include "posixver.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
enum { MAX = 100 };
int main(void)
{
if (freopen("input.txt", "r", stdin) == 0)
return 1;
char s[MAX];
for (int i = 0; i < 30 && fgets(s, MAX, stdin) != NULL; i++)
{
// Commenting out this region fixes the issue
int status;
pid_t pid = fork();
if (pid == 0)
{
exit(0);
}
else
{
waitpid(pid, &status, 0);
}
// End region
printf("%s", s);
}
return 0;
}
One of the modifications limits the number of cycles (children) to just 30.
I used a data file with 4 lines of 20 random letters plus a newline (84 bytes total):
ywYaGKiRtAwzaBbuzvNb
eRsjPoBaIdxZZtJWfSty
uGnxGhSluywhlAEBIXNP
plRXLszVvPgZhAdTLlYe
I ran the command under strace on Ubuntu:
$ strace -ff -o st-out -- neof97
ywYaGKiRtAwzaBbuzvNb
eRsjPoBaIdxZZtJWfSty
uGnxGhSluywhlAEBIXNP
plRXLszVvPgZhAdTLlYe
…
uGnxGhSluywhlAEBIXNP
plRXLszVvPgZhAdTLlYe
ywYaGKiRtAwzaBbuzvNb
eRsjPoBaIdxZZtJWfSty
$
There were 31 files with names of the form st-out.808## where the hashes were 2-digit numbers. The main process file was quite large; the others were small, with one of the sizes 66, 110, 111, or 137:
$ cat st-out.80833
lseek(0, -63, SEEK_CUR) = 21
exit_group(0) = ?
+++ exited with 0 +++
$ cat st-out.80834
lseek(0, -42, SEEK_CUR) = -1 EINVAL (Invalid argument)
exit_group(0) = ?
+++ exited with 0 +++
$ cat st-out.80835
lseek(0, -21, SEEK_CUR) = 0
exit_group(0) = ?
+++ exited with 0 +++
$ cat st-out.80836
exit_group(0) = ?
+++ exited with 0 +++
$
It just so happened that the first 4 children each exhibited one of the four behaviours — and each further set of 4 children exhibited the same pattern.
This shows that three out of four of the children were indeed doing an lseek() on standard input before exiting. Obviously, I have now seen a library do it. I have no idea why it is thought to be a good idea, though, but empirically, that is what is happening.
neof67.c
This version of the code, using a separate file stream (and file descriptor) and fopen() instead of freopen() also runs into the problem.
#include "posixver.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
enum { MAX = 100 };
int main(void)
{
FILE *fp = fopen("input.txt", "r");
if (fp == 0)
return 1;
char s[MAX];
for (int i = 0; i < 30 && fgets(s, MAX, fp) != NULL; i++)
{
// Commenting out this region fixes the issue
int status;
pid_t pid = fork();
if (pid == 0)
{
exit(0);
}
else
{
waitpid(pid, &status, 0);
}
// End region
printf("%s", s);
}
return 0;
}
This also exhibits the same behaviour, except that the file descriptor on which the seek occurs is 3 instead of 0. So, two of my hypotheses are disproven — it's related to freopen() and stdin; both are shown incorrect by the second test code.
Preliminary diagnosis
IMO, this is a bug. You should not be able to run into this problem.
It is most likely a bug in the Linux (GNU C) library rather than the kernel. It is caused by the lseek() in the child processes. It is not clear (because I've not gone to look at the source code) what the library is doing or why.
GLIBC Bug 23151
GLIBC Bug 23151 - A forked process with unclosed file does lseek before exit and can cause infinite loop in parent I/O.
The bug was created 2018-05-08 US/Pacific, and was closed as INVALID by 2018-05-09. The reason given was:
Please read
http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_01,
especially this paragraph:
Note that after a fork(), two handles exist where one existed before. […]
POSIX
The complete section of POSIX referred to (apart from verbiage noting that this is not covered by the C standard) is this:
2.5.1 Interaction of File Descriptors and Standard I/O Streams
An open file description may be accessed through a file descriptor, which is created using functions such as open() or pipe(), or through a stream, which is created using functions such as fopen() or popen(). Either a file descriptor or a stream is called a "handle" on the open file description to which it refers; an open file description may have several handles.
Handles can be created or destroyed by explicit user action, without affecting the underlying open file description. Some of the ways to create them include fcntl(), dup(), fdopen(), fileno(), and fork(). They can be destroyed by at least fclose(), close(), and the exec functions.
A file descriptor that is never used in an operation that could affect the file offset (for example, read(), write(), or lseek()) is not considered a handle for this discussion, but could give rise to one (for example, as a consequence of fdopen(), dup(), or fork()). This exception does not include the file descriptor underlying a stream, whether created with fopen() or fdopen(), so long as it is not used directly by the application to affect the file offset. The read() and write() functions implicitly affect the file offset; lseek() explicitly affects it.
The result of function calls involving any one handle (the "active handle") is defined elsewhere in this volume of POSIX.1-2017, but if two or more handles are used, and any one of them is a stream, the application shall ensure that their actions are coordinated as described below. If this is not done, the result is undefined.
A handle which is a stream is considered to be closed when either an fclose(), or freopen() with non-full(1) filename, is executed on it (for freopen() with a null filename, it is implementation-defined whether a new handle is created or the existing one reused), or when the process owning that stream terminates with exit(), abort(), or due to a signal. A file descriptor is closed by close(), _exit(), or the exec() functions when FD_CLOEXEC is set on that file descriptor.
(1) [sic] Using 'non-full' is probably a typo for 'non-null'.
For a handle to become the active handle, the application shall ensure that the actions below are performed between the last use of the handle (the current active handle) and the first use of the second handle (the future active handle). The second handle then becomes the active handle. All activity by the application affecting the file offset on the first handle shall be suspended until it again becomes the active file handle. (If a stream function has as an underlying function one that affects the file offset, the stream function shall be considered to affect the file offset.)
The handles need not be in the same process for these rules to apply.
Note that after a fork(), two handles exist where one existed before. The application shall ensure that, if both handles can ever be accessed, they are both in a state where the other could become the active handle first. The application shall prepare for a fork() exactly as if it were a change of active handle. (If the only action performed by one of the processes is one of the exec() functions or _exit() (not exit()), the handle is never accessed in that process.)
For the first handle, the first applicable condition below applies. After the actions required below are taken, if the handle is still open, the application can close it.
If it is a file descriptor, no action is required.
If the only further action to be performed on any handle to this open file descriptor is to close it, no action need be taken.
If it is a stream which is unbuffered, no action need be taken.
If it is a stream which is line buffered, and the last byte written to the stream was a <newline> (that is, as if a
putc('\n')
was the most recent operation on that stream), no action need be taken.
If it is a stream which is open for writing or appending (but not also open for reading), the application shall either perform an fflush(), or the stream shall be closed.
If the stream is open for reading and it is at the end of the file (feof() is true), no action need be taken.
If the stream is open with a mode that allows reading and the underlying open file description refers to a device that is capable of seeking, the application shall either perform an fflush(), or the stream shall be closed.
For the second handle:
If any previous active handle has been used by a function that explicitly changed the file offset, except as required above for the first handle, the application shall perform an lseek() or fseek() (as appropriate to the type of handle) to an appropriate location.
If the active handle ceases to be accessible before the requirements on the first handle, above, have been met, the state of the open file description becomes undefined. This might occur during functions such as a fork() or _exit().
The exec() functions make inaccessible all streams that are open at the time they are called, independent of which streams or file descriptors may be available to the new process image.
When these rules are followed, regardless of the sequence of handles used, implementations shall ensure that an application, even one consisting of several processes, shall yield correct results: no data shall be lost or duplicated when writing, and all data shall be written in order, except as requested by seeks. It is implementation-defined whether, and under what conditions, all input is seen exactly once.
Each function that operates on a stream is said to have zero or more "underlying functions". This means that the stream function shares certain traits with the underlying functions, but does not require that there be any relation between the implementations of the stream function and its underlying functions.
Exegesis
That is hard reading! If you're not clear on the distinction between open file descriptor and open file description, read the specification of open() and fork() (and dup() or dup2()). The definitions for file descriptor and open file description are also relevant, if terse.
In the context of the code in this question (and also for Unwanted child processes being created while file reading), we have a file stream handle open for reading only which has not yet encountered EOF (so feof() would not return true, even though the read position is at the end of the file).
One of the crucial parts of the specification is: The application shall prepare for a fork() exactly as if it were a change of active handle.
This means that the steps outlined for 'first file handle' are relevant, and stepping through them, the first applicable condition is the last:
If the stream is open with a mode that allows reading and the underlying open file description refers to a device that is capable of seeking, the application shall either perform an fflush(), or the stream shall be closed.
If you look at the definition for fflush(), you find:
If stream points to an output stream or an update stream in which the most recent operation was not input, fflush() shall cause any unwritten data for that stream to be written to the file, [CX] ⌦ and the last data modification and last file status change timestamps of the underlying file shall be marked for update.
For a stream open for reading with an underlying file description, if the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream, and any characters pushed back onto the stream by ungetc() or ungetwc() that have not subsequently been read from the stream shall be discarded (without further changing the file offset). ⌫
It isn't exactly clear what happens if you apply fflush() to an input stream associated with a non-seekable file, but that isn't our immediate concern. However, if you're writing generic library code, then you might need to know whether the underlying file descriptor is seekable before doing a fflush() on the stream. Alternatively, use fflush(NULL) to have the system do whatever is necessary for all I/O streams, noting that this will lose any pushed-back characters (via ungetc() etc).
The lseek() operations shown in the strace output seem to be implementing the fflush() semantics associating the file offset of the open file description with the file position of the stream.
So, for the code in this question, it seems that fflush(stdin) is necessary before the fork() to ensure consistency. Not doing that leads to undefined behaviour ('if this is not done, the result is undefined') — such as looping indefinitely.
The exit() call closes all open file handles. After the fork, the child and parent have identical copies of the execution stack, including the FileHandle pointer. When the child exits, it closes the file and resets the pointer.
int main(){
freopen("input.txt", "r", stdin);
char s[MAX];
prompt(s);
int i = 0;
char* ret = fgets(s, MAX, stdin);
while (ret != NULL) {
//Commenting out this region fixes the issue
int status;
pid_t pid = fork(); // At this point both processes has a copy of the filehandle
if (pid == 0) {
exit(0); // At this point the child closes the filehandle
} else {
waitpid(pid, &status, 0);
}
//End region
printf("%s", s);
ret = fgets(s, MAX, stdin);
}
}
As /u/visibleman pointed out, the child thread is closing the file and messing things up in main.
I was able to work around it by checking if the program is in terminal mode with
!isatty(fileno(stdin))
And if stdin has been redirected, then it will read all of it into a linkedlist before doing any processing or forking.
Replace exit(0) with _exit(0), and all is fine. This is an old unix tradition, if you are using stdio, your forked image must use _exit(), not exit().

MULTITHREADING c - read several files in the same file

I'm new at multithreading and I'm trying to simulate banking transactions on the same current account using multithreading.
Each thread reads the actions to perform from a file. The file will contain an operation for each line consisting of an integer. The main program have to create as many threads as files in the path.
int main(int argc,char*argv[]){
DIR *buff;
struct dirent *dptr = NULL;
pthread_t hiloaux[MAX_THREADS];
int i=0,j=0, nthreads=0;
char *pathaux;
memset(hiloaux,0,sizeof(pthread_t)*MAX_THREADS);
diraux=malloc((267+strlen(argv[1]))*sizeof(char));
buff=opendir(argv[1]);
while((dptr = readdir(buff)) != NULL && nthreads<MAX_THREADS)//read files in the path
{
if (dptr->d_name[0]!='.'){
pthread_mutex_lock(&mutex_path);//mutual exclusion
strcpy(pathaux,argv[1]);
strcat (pathaux,"/");
strcat (pathaux,dptr->d_name);//makes the route (ex:path/a.txt)
pthread_create(&hiloaux[nthreads],NULL,readfile,(void *)pathaux);
//creates a thread for each file in the path
nthreads++;
}
}
for (j=0;j<nthreads;j++){
pthread_join(hiloaux[j],NULL);
}
closedir(buff);
return 0;
}
My problem is that the threads don't seem to receive the correct path argument. Even though I have placed a mutex, (mutex_path) they all read the same file. I unlock this mutex inside the function readfile(), .
void *readfile(void *arg){
FILE *fichero;
int x=0,n=0;
int suma=0;
int cuenta2=0;
char * file_path = (char*)arg;
n=rand() % 5+1; //random number to sleep the program each time I read a file line
pthread_mutex_unlock(&mutex_path);//unlock the mutex
fichero = fopen(file_path, "r");
while (fscanf (fichero, "%d", &x)!=EOF){
pthread_mutex_lock(&mutex2);//mutual exclusion to protect variables(x,cuenta,cuenta2)
cuenta2+=x;
if (cuenta2<0){
printf("count discarded\n");
}
else cuenta=cuenta2;
pthread_mutex_unlock(&mutex2);
printf("sum= %d\n",cuenta);
sleep(n); //Each time i read a line,i sleep the thread and let other thread read other fileline
}
pthread_exit(NULL);
fclose(fichero);
}
When I run the program i get this output
alberto#ubuntu:~/Escritorio/practica3$ ./ejercicio3 path
read file-> path/fb
read -> 2
sum= 2
read file-> path/fb
read -> 2
sum= 2
read file-> path/fb
read -> 4
sum= 6
read file-> path/fb
read -> 4
sum= 6
read file-> path/fb
read -> 6
sum= 12
read file-> path/fb
read -> 6
sum= 12
It seems to work well, it reads a line and sleeps for a time, during this time another thread do its work, but the problem is that both threads open the same file (path/fb).
As i said before i think the problem is in path argument, is like the mutex_path did not make his work.
I would really appreciate a little help with this, as I don't really know what's wrong.
Thank you very much.
In your "readfile" function
the line
char * file_path = (char*)arg;
Just copies a pointer the string memory but not the memory itself.
So it can (and will) still be altered by the man thread while worker thread continues.
Make a memory copy there.
Or even better store all arguments to your threads in distinct memory in main thread already, so you wont need the first mutex at all.
First I do not see where u assign memory for pathaux. I am wondering how come strcpy or strcat is working rather than memory segmentation. Try compiling with C++ compiler and it may complain.
As to the problem u are passing the pointer, so every thread points to same location.
Correct approach would be inside readdir loop -
1. create memory and copy the path to it.(Note u want to create memory every time in loop)
2. pass this memory to new thread.
If you do this way :
a. you do not have to use mutex path.
b. call free at end of readfile method.

Multiple processes accessing the same file

Is it alright for multiple processes to access (write) to the same file at the same time? Using the following code, it seems to work, but I have my doubts.
Use case in the instance is an executable that gets called every time an email is received and logs it's output to a central file.
if (freopen(console_logfile, "a+", stdout) == NULL || freopen(error_logfile, "a+", stderr) == NULL) {
perror("freopen");
}
printf("Hello World!");
This is running on CentOS and compiled as C.
Using the C standard IO facility introduces a new layer of complexity; the file is modified solely via write(2)-family of system calls (or memory mappings, but that's not used in this case) -- the C standard IO wrappers may postpone writing to the file for a while and may not submit complete requests in one system call.
The write(2) call itself should behave well:
[...] If the file was
open(2)ed with O_APPEND, the file offset is first set to the
end of the file before writing. The adjustment of the file
offset and the write operation are performed as an atomic
step.
POSIX requires that a read(2) which can be proved to occur
after a write() has returned returns the new data. Note that
not all file systems are POSIX conforming.
Thus your underlying write(2) calls will behave properly.
For the higher-level C standard IO streams, you'll also need to take care of the buffering. The setvbuf(3) function can be used to request unbuffered output, line-buffered output, or block-buffered output. The default behavior changes from stream to stream -- if standard output and standard error are writing to the terminal, then they are line-buffered and unbuffered by default. Otherwise, block-buffering is the default.
You might wish to manually select line-buffered if your data is naturally line-oriented, to prevent interleaved data. If your data is not line-oriented, you might wish to use un-buffered or leave it block-buffered but manually flush the data whenever you've accumulated a single "unit" of output.
If you are writing more than BUFSIZ bytes at a time, your writes might become interleaved. The setvbuf(3) function can help prevent the interleaving.
It might be premature to talk about performance, but line-buffering is going to be slower than block buffering. If you're logging near the speed of the disk, you might wish to take another approach entirely to ensure your writes aren't interleaved.
This answer was incorrect. It does work:
So the race condition would be:
process 1 opens it for append, then
later process 2 opens it for append, then
later still 1 writes and closes, then
finally 2 writes and closes.
I'd be impressed if that 'worked' because it isn't clear to me what
working should mean. I assume 'working' means all of the bytes written
by the two processes are inthe log file? I'd expect that they both
write starting at the same byte offset, so one will replace the others
bytes. It will all be okay upto and including step 3. and only show up
as a problem at step 4, Seems like an easy test to write: open getchar
... write close.
Is it critical that they can have the file open simultaneously? A
more obvious solution if the write is quick, is to open exclusive.
For a quick check on your system, try:
/* write the first command line argument to a file called foo
* stackoverflow topic 9880935
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main (int argc, const char * argv[]) {
if (argc <2) {
fprintf(stderr, "Error: need some text to write to the file Foo\n");
exit(1);
}
FILE* fp = freopen("foo", "a+", stdout);
if (fp == NULL) {
perror("Error failed to open file\n");
exit(1);
}
fprintf(stderr, "Press a key to continue\n");
(void) getchar(); /* Yes, I really mean to ignore the character */
if (printf("%s\n", argv[1]) < 0) {
perror("Error failed to write to file: ");
exit(1);
}
fclose(fp);
return 0;
}

Resources