Read and Write operations get bad fd and no such file - c

I have this C program.
I have two processes, father and son, and use semaphores to make them synchronize one at time.
The father has to write (n) numbers, ten in this case, always in the first byte of the opened file and the son has to read it.
The problem is that when I print the results, I get bad file descriptor for the write (father) and no such file for the read(the son).
Can you help me, please?? Thank you
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "test.txt"
#define MUTEX "/mutex"
#define READ "/read"
#define WRITE "/write"
int main(int argc, char *argv[]){
int i, pid, n=10, fd, x;
int nread, nwrite;
char c = 'a';
sem_t *mutex, *reader, *writer;
//fd = open(FILENAME, O_CREAT | O_TRUNC, 0666);
mutex = sem_open(MUTEX, O_CREAT, 0666, 1);
reader = sem_open(READ, O_CREAT, 0666, 0);
writer = sem_open(WRITE, O_CREAT, 0666, 1);
pid = fork();
fd = open(FILENAME, O_CREAT | O_TRUNC, 0777);
if(fd < 0){
perror("Open FILE error");
exit(-1);}
if(pid == 0){ // son
do{
sem_wait(reader); // si può leggere???
sem_wait(mutex);
lseek(fd, 0, SEEK_SET);
nread = read(fd, &x, sizeof(int));
if(nread <=0)
perror("Read error");
printf("Son has read (%d byte) = %d\n", nread, x);
fflush(NULL);
sem_post(mutex);
sem_post(writer);
}
while(x != (n-1));
exit(0);
}
else{
for(i=0; i<n; i++){
sem_wait(writer); // can I write??
sem_wait(mutex);
lseek(fd, 0, SEEK_SET);
nwrite = write(fd, &c, sizeof(char));
if(nwrite <= 0)
perror("nwrite error");
printf("Father has written (%d byte) %d\n", nwrite, i);
fflush(NULL);
sem_post(mutex);
sem_post(reader); // it's possible to read
}
//wait(NULL);
}
sem_unlink(MUTEX);
sem_unlink(READ);
sem_unlink(WRITE);
//remove(FILENAME);
exit(0);
}

First, you opened the file without specifying an o_flag. That's actually undefined behavior ("Applications shall specify exactly one of .... O_RDONLY .... O_WRONLY .... O_RDWR"), but for practical purposes means the file was opened read only.
Thus the parent's write operation fails with EBADF. Can't write to a read only file!
Second, the child's error checking is incorrect. read() may return zero on success, in which case errno, consulted by perror(), is not guaranteed to be meaningful. You mean to check for a return value of less than zero, not of less than or equal to zero.

Your open() call is opening the file for read only. You have:
fd = open(FILENAME, O_CREAT | O_TRUNC, 0777);
Because you don't explicitly say O_WRONLY or O_RDWR, and because the traditional value for O_RDONLY is 0, you are effectively opening the file read-only.
The 0777 permissions are suspect too. You are not creating an executable; you should not be giving the file executable permissions. In my book, you probably shouldn't be giving others write permission on the file. In fact, I'd probably go with 0600 permissions.

Your program is a bit strange.
First off, you are asking for trouble by having parent and child processes race to be the one to create the target file. It would be better for the parent to create and open the file before forking, and for (only) the child to open it after. Be aware that in that case the child should first close (its copy of) the file descriptor opened by the parent. Alternatively, the way you're doing things, if the parent opened the file then it would probably be sufficient for the child to just use the file descriptor it inherits, without opening the file itself at all.
With that said, it would be more usual for parent and child processes to communicate via a pipe than via a physical file. That has the particular advantage that you do not need to synchronize access via a semaphore; ordinary blocking I/O does the job.
Additionally, I don't see what your mutex semaphore is doing for you. Even with a design that requires you to manually synchronize writing and reading, it looks like your reader and writer semaphores will serve that purpose without help.
Importantly, your parent process is writing in sizeof(char)-byte uints, whereas your child process is trying to read in sizeof(int)-byte units. This is unlikely to have the result you want.
Furthermore, the read() and write() functions may return successfully without having transferred the full number of bytes requested (unless that number is 1 and the file is open in blocking mode). You need to account for that by being prepared to use multiple I/O operations to transfer multi-byte data, if necessary.
Finally, it would be best for just one process to unlink your semaphores. It is ok for that to happen while the other process is still running.

Related

Can I have multiple POSIX FIFOs in a single process?

I am creating three FIFO pipes for a single process where multiple writer threads write to a single reader thread. The goal is a separate file descriptor for each writer thread with a corresponding reader file descriptor for each writer file descriptor so that I know which writer thread belongs to each record. My setup works fine with one writer file descriptor shared among all writer threads and a single reader file descriptor.
The multiple reader/writer setup works successfully to set up three writer fds (6, 7, 8) and three reader fds (9, 10 and 11). All writer threads successfully send data, BUT the reader fds do not successfully read the data (return zero bytes read).
When I open the fd on the writer side, I get the error message "open: Bad file descriptor" but it still returns a unique fd, and that fd is used successfully to write, but not to read.
I call the C programs from NASM in a loop, one iteration for each writer thread:
xor r12,r12
mov r13,[Number_Of_Cores_Calc]
Setup_pipe:
lea rdi,[fifo_base_name]
mov rax,r12
add rax,48
mov byte[rdi+11],al
call fifo_delete wrt ..plt
call fifo_setup wrt ..plt
push rdi
call fifo_close wrt ..plt
pop rdi
Open_FIFO_Write:
mov rsi,1
call fifo_open wrt ..plt
lea rbp,[fifo_write_fds]
mov [rbp+r12*8],rax
add r12,1
cmp r12,r13
jl Setup_pipe
The corresponding C programs:
int64_t fifo_setup(char * fifo_name)
{
remove(fifo_name);
if (mkfifo(fifo_name, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) == -1) {
perror("mkfifo");
exit(EXIT_FAILURE); }
return 0;
}
int64_t fifo_open(char * fifo_name, int64_t read_write) {
int c;
if (read_write == 1) {
c = open(fifo_name, O_CREAT | O_RDWR);} //O_WRONLY
if (read_write == 2) {
c = open(fifo_name, O_CREAT | O_RDWR);} //O_RDONLY
perror("open");
return (c);
}
int64_t fifo_read(int fd, void *buf, size_t count) {
int status_read = read(fd, buf, count);
return status_read;
}
int64_t fifo_write(int fd, const void *buf, size_t count) {
int status_write = write(fd, buf, count);
return status_write; }
int64_t fifo_close(int fifo_fdes) {
close(fifo_fdes);
return 0; }
// Delete pre-existing file
int64_t fifo_delete(char * fifo_name) {
if( access( fifo_name, F_OK ) != -1 ) {
if (remove(fifo_name) == 0)
printf("File deleted successfully");
else
printf("Unable to delete the file");
}
return 0; }
So my questions are:
Can I write from multiple threads to a single reader thread using separate fds for each thread on both the write and read sides?
If I can, what did I do wrong above -- especially why am I getting "open: Bad file descriptor" but I still get what looks like a valid file descriptor.
If I can't use POSIX FIFOs, what IPC methods can I use so each writer thread has its own unique fd on both writer and reader sides? Datagram sockets?
Thanks for any help on this.
perror("open");
You cannot simply call perror() as it can return the previous error.
You have to check whether your syscall indeed generated an error. In case of open() you should do:
if (c < 0) {
perror("open");
}
If all the communication is going to be between threads in a single process, you can use pipe(2) system call and give fd[0] to the reader thread while giving fd[1] to the writer. You don't even need to open() or have a name in the filesytem associated to the fifo. You still will need to close fd[1] if you want to signal end of file to the reader.

Cannot Write in Named Pipe

In this code my program crashes in when I am opening the pipe for writing.
char pipe[30];
int fd, tmp = 2;
sprintf(pipe, "root_%d", getpid());
ret_val = mkfifo(pipe, 0666);
fd = open(pipe, O_WRONLY); //HERE IS CRASHING - SUDDENLY FREEZES
write(fd, &tmp, sizeof(int));
close(fd)
All seems good, but where is my mistake;
It is an expected behavior. From man 7 fifo:
Normally, opening the FIFO blocks until the other end is opened also.
So your open does not return until somebody opens the same pipe for reading. You may want to add O_NONBLOCK flag (and likely get SIGPIPE on writing), or revisit the design.

Writing string to pipe

I am trying to send a string to a pipe in unix. When I go through a line-by-line debugging process, the call mkfifo() creates the file in the same directory as the source code. However, when I reach the open() call, the debugger is no longer able to proceed. I'm not sure why it is unable to access the pipe file.
Here's the code in question:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char * myfifo = "myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hi", sizeof("Hi"));
close(fd);
/* remove the FIFO */
unlink(myfifo);
return 0;
}
Any suggestions are appreciated. Thank you.
Normally a FIFO has to be open at both ends simultaneously before either side can proceed. Since you didn't mention anything about a reader, the most likely answer is that you haven't got one, or you haven't set it up yet. Once you do, the open will be allowed to proceed.
mkfifo(3) routes to fifo(7) which reads:
The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also.
There is a solution for non-blocking read:
A process can open a FIFO in nonblocking mode. In this case, opening for read only will succeed even if no-one has opened on the write side yet, opening for write only will fail with ENXIO (no such device or address) unless the other end has already been opened.
So you could fork another process for reading:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
long strlen(char * c){
return c[0] == 0 ? 0 : 1 + strlen(++c);
}
int main()
{
int fd;
int fr;
char buf[3];
char * MESSAGE = "Hi\n";
char * myfifo = "myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
int msglen = strlen(MESSAGE);
int child = fork();
if (child == 0){
/* read "Hi" from the FIFO (CHILD)*/
fr = open(myfifo, O_RDONLY);
read(fr, buf, msglen);
write(1, buf, msglen);
close(fr);
} else {
/* write "Hi" to the FIFO (PARENT)*/
fd = open(myfifo, O_WRONLY);
write(fd, MESSAGE, sizeof(char) * msglen);
close(fd);
/* remove the FIFO */
wait(child);
unlink(myfifo);
}
return 0;
}
I guess you have to open both ends before you write.
here is an excerpt from the man page for mkfifo() See my notes at the end
mkfifo() makes a FIFO special file with name pathname. mode
specifies the FIFO's permissions. It is modified by the process's
umask in the usual way: the permissions of the created file are (mode
& ~umask).
A FIFO special file is similar to a pipe, except that it is created
in a different way. Instead of being an anonymous communications
channel, a FIFO special file is entered into the filesystem by
calling mkfifo().
Once you have created a FIFO special file in this way, any process
can open it for reading or writing, in the same way as an ordinary
file. However, it has to be open at both ends simultaneously before
you can proceed to do any input or output operations on it. Opening
a FIFO for reading normally blocks until some other process opens the
same FIFO for writing, and vice versa. See fifo(7) for nonblocking
handling of FIFO special files.
Notes: There are two details of importance:
1) the 'mode' parameter is modified by the value of 'umask'
2) both ends of the fifo must be open at the same time
before any I/O operations can be performed.

why non-blocking write to disk doesn't return EAGAIN or EWOULDBLOCK?

I modified a program from APUE, the program first open a file, then mark the fd as non-blocking, then continue write to the fd until write return -1.
I think since disk I/O is slow, when write buffers in OS is nearly full, the write system call will return -1, and the errno should be EAGAIN or EWOULDBLOCK.
But I ran the program for about several minutes and I repeated running the program serveral times, the write system call didn't returned -1 even once! Why?
Here's the code:
#include "apue.h"
#include <errno.h>
#include <fcntl.h>
char buf[4096];
int
main(void)
{
int nwrite;
int fd = open("a.txt", O_RDWR);
if(fd<0){
printf("fd<0\n");
return 0;
}
int i;
for(i = 0; i<sizeof(buf); i++)
buf[i] = i*2;
set_fl(fd, O_NONBLOCK); /* set nonblocking */
while (1) {
nwrite = write(fd, buf, sizeof(buf));
if (nwrite < 0) {
printf("write returned:%d, errno=%d\n", nwrite, errno);
return 0;
}
}
clr_fl(STDOUT_FILENO, O_NONBLOCK); /* clear nonblocking */
exit(0);
}
The O_NONBLOCK flag is primarily meaningful for file descriptors representing streams (e.g, pipes, sockets, and character devices), where it prevents read and write operations from blocking when there is no data waiting to read, or buffers are too full to write anything more at the moment. It has no effect on file descriptors opened to regular files; disk I/O delays are essentially ignored by the system.
If you want to do asynchronous I/O to files, you may want to take a look at the POSIX AIO interface. Be warned that it's rather hairy and infrequently used, though.

open with O_CREAT - was it opened or created?

I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it. Is there any robust way to find out which process actually did create the file and which did open already create file, for instance, if I want to accurately count how many times that file was opened in such scenario.
I guess I could put a global mutex on file open operation, and do a sequence of open() calls using O_CREAT and O_EXCL flags, but that doesn't fit my definition of "robust".
Use O_EXCL flag with O_CREAT. This will fail if the file exists and errno will be set to EEXIST. If it does fail
then attempt open again without O_CREAT and without O_EXCL modes.
e.g.
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if ((fd == -1) && (EEXIST == errno))
{
/* open the existing file with write flag */
fd = open(path, O_WRONLY);
}
Based roughly on your comments, you want something along the lines of this function:
/* return the fd or negative on error (check errno);
how is 1 if created, or 0 if opened */
int create_or_open (const char *path, int create_flags, int open_flags,
int *how) {
int fd;
create_flags |= (O_CREAT|O_EXCL);
open_flags &= ~(O_CREAT|O_EXCL);
for (;;) {
*how = 1;
fd = open(path, create_flags);
if (fd >= 0) break;
if (errno != EEXIST) break;
*how = 0;
fd = open(path, open_flags);
if (fd >= 0) break;
if (errno != ENOENT) break;
}
return fd;
}
This solution is not bullet proof. There may be cases (symbolic links maybe?) that would cause it to loop forever. Also, it may live-lock in certain concurrency scenarios. I'll leave resolving such issues as an exercise. :-)
In your edited question, you pose:
I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it.
A hack-ish, but more bullet proof, solution would be to give each process a different user ID. Then, just use the regular open(path, O_CREAT|...) call. You can then query the file with fstat() on the file descriptor, and check the st_uid field of the stat structure. If the field equals the processes' user ID, then it was the creator. Otherwise, it was an opener. This works since each process deletes the file after opening.

Resources