I wan't to write a program that uses 2 pipes to pass values between parent and child process.Code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkfifo(const char *path, mode_t mode);
int main(int argc, char **argv)
{
int fd,df,val;
char * myfifo = "/tmp/myfifo";
mkfifo(myfifo, 0666);
char * myfifo2 = "/tmp/myfifo2";
mkfifo(myfifo2, 0666);
pid_t c=fork();
if(c==0){
df = open(myfifo2, O_RDONLY);
read(df, &val, sizeof(val));
close(df);
val= val+10;
df = open(myfifo2, O_WRONLY);
write(df, &val, sizeof(val));
fd = open(myfifo, O_WRONLY); //if i put this write first print works and program finishes
write(fd, &val, sizeof(val));
}
if(c>0)
{
val=1;
df = open(myfifo2, O_WRONLY);
write(df, &val, sizeof(val));
fd = open(myfifo, O_RDONLY);
read(fd, &val, sizeof(val));
printf("val is %d \n",val);
}
}
The first read of the child waits for parent to write val inside the fifo file.After that it writes a new val into this fifo file and afterwards writes a value into the second text file.The read in parent's code waits for this write to happen.So,all in all what i want is the parent process to execute the print only after the child process has written val in myfifo2 file.That is the purpose of the second named pipe i have used.However,this code gets stuck and prints nothing.I noticed that if i change the turn of the two write in child process it works but it does not satisfy the above thing i need.Why is this happening and how do i solve this?
What's happening
While named pipes can be used with buffering (as you intend them to), relying on it to not block your program (because that's what you have, a deadlock) is a really bad practice.
Moreover, it comes with some caveats:
In order to have that "buffering" effect, the both ends of the pipe need to be opened (man 7 fifo)
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.
Writes will eventually block once the pipe reaches it capacity. This size depends on the system but can be set via F_SETPIPE_SZ fcntl. But also:
Note that because of the way the pages of the pipe buffer are
employed when data is written to the pipe, the number of bytes
that can be written may be less than the nominal size, dependā
ing on the size of the writes.
So, you have this situation:
Parent (probably, could be even not yet there) waits in read on /tmp/myfifo.
/tmp/myfifo2 is opened WR_ONLY by parent.
child calls df = open(myfifo2, O_WRONLY);, opening /tmp/myfifo2 also as WR_ONLY. Thus the read end of the pipe is not opened, therefore the call to open blocks.
Deadlock.
To solve this, opening myfifo2 in the child as read-write should be sufficient.
df = open(myfifo2, O_RDWR); // instead of df = open(myfifo2, O_WRONLY);
IMO this communication pattern looks a bit strange (and hard to understand), though.
Related
I have written this simple program:
#include<stdio.h>
#include<unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main(){
int fd = open("theFile.txt", O_CREAT | O_RDWR, 0666);
if(fd<0){
printf("coudlnt open File descriptor \n");
}
pid_t pid = fork();
if(pid==0){
dup2(int oldFD, int newFD);
dup2(fd,1);
execlp("/bin/ls","ls","-l", NULL);
}
return 0;
}
What i want is, redirect the output of ls - l to a file called "theFile.txt". The code works as i expect. What confuses me here is the order of dup2 parameters. I believe that correct order should be dup2(1, fd) - considering fd as the newFD and 1 as the oldFD. But the code works when i use it as dup2(fd,1) which basically is stdout to fd according to some other answers on SO.
How is the oldFD fd here and how is the newFD 1 here? If 1 is newFD, why does this program work in the first place?
Also, execlp overwrites child's address space after I call dup2. How is dup2 connected to execlp such that the desired result is obtained. i.e. what i do cat theFile.txt i get the current directly listed.
Can i get some explanation here please?
According to [man7]: DUP(2):
int dup2(int oldfd, int newfd);
...
The dup() system call creates a copy of the file descriptor oldfd,
using the lowest-numbered unused file descriptor for the new
descriptor.
...
The dup2() system call performs the same task as dup(), but instead
of using the lowest-numbered unused file descriptor, it uses the file
descriptor number specified in newfd. If the file descriptor newfd
was previously open, it is silently closed before being reused.
When outputing data (e.g. text) to console, applications use the stdout stream (also stderr, but for simplicity's sake, let's leave that out). stdout's fileno is 1 (it's better to use constants instead of values, as values might change - not very likely in this case, but in general):
cfati#cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q048923791$ cat /usr/include/unistd.h | grep STDOUT
#define STDOUT_FILENO 1 /* Standard output. */
In your child process, ls (via execlp) spits its data to stdout (fileno 1). Before that, the dup2 call is made. The current situation, before the dup2 call (for clarity, I'm going to use the defined macro when referring to stdout's fileno):
fd: points to the custom file (opened previously)
STDOUT_FILENO: points to stdout
The dup2 call:
dup2(fd, STDOUT_FILENO) (as it is now): closes current STDOUT_FILENO and duplicates fd to STDOUT_FILENO. Current situation:
fd: points to the custom file
STDOUT_FILENO: points to the custom file
dup2(STDOUT_FILENO, fd): closes current fd and duplicates STDOUT_FILENO to fd. Current situation:
fd: points to stdout
STDOUT_FILENO: points to stdout
As seen, for #1., when data will be output to stdout, it will actually go to the custom file (as opposed to #2. where it would go to stdout, even when using fd).
Regarding the 2nd question:
[man7]: EXEC(3):
The exec() family of functions replaces the current process image
with a new process image. The functions described in this manual
page are front-ends for execve(2).
[man7]: EXECVE(2):
By default, file descriptors remain open across an execve().
...
POSIX.1 says that if file descriptors 0, 1, and 2 would
otherwise be closed after a successful execve(), and the process
would gain privilege because the set-user-ID or set-group_ID mode
bit was set on the executed file, then the system may open an
unspecified file for each of these file descriptors. As a general
principle, no portable program, whether privileged or not, can
assume that these three file descriptors will remain closed across
an execve().
The file descriptors will be passed from the child process to ls.
Here's an improved version (minor changes only) of your code (code00.c):
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main() {
int ret = 0, fd = open("thefile.txt", O_CREAT | O_RDWR, 0666);
if (fd < 0) {
printf("Coudln't open file: %d\n", errno);
ret = 1;
}
pid_t pid = fork();
if (pid == 0) {
// dup2(int oldFD, int newFD);
if (dup2(fd, STDOUT_FILENO) < 0) {
printf("Couldn't redirect stdout: %d\n", errno);
ret = 2;
}
execlp("/bin/ls", "ls", "-l", NULL);
} else if (pid < 0) {
printf("Couldn't spawn child process: %d\n", errno);
ret = 3;
}
return ret;
}
I am trying to prove one of my doubts, that two non-related processes can share the fd of half-duplex pipe and have communication.
I have created two programs for that. But then I had this another question, that what happens to the pipe if process dies?Because my reader got some garbage message, when I printed out the message.
Writer
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
char str[] = "hello\n";
if(pipe(fd) < 0)
perror("Pipe creation failed\n");
//Since i am a writer, i should close the reading end as a best practice
close(fd[0]);
/*
The processes need not to be related processes, in order to use the half duplex pipes. fd is just a number/identifier
which can be shared across different processes
*/
printf("Hey there !!! use this file descriptor for reading : %d\n", fd[0]);
//writing message
write(fd[1],str,strlen(str)+1);
return 0;
}
Reader
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd,bytesRead;
char buffer[1024];
printf("please enter the fd :");
scanf("%d",&fd);
bytesRead = read(fd,buffer,1024);
printf("Bytes Read : %d\nMessage : %s\n", bytesRead, buffer);
return 0;
}
You can't do this.
The table of file descriptors is per-process; every process has its own separate set of open file descriptors (note the distinction between open file descriptors, which are per-process, and open file descriptions, which are system-wide, discussed in open(2)). If you want to share a file descriptor between processes, you need to either inherit it over a fork(2) or pass it through a unix(7) domain socket via sendmesg(2) with SCM_RIGHTS in the cmesg(3) header.
(On Linux, you can also pass around paths to /proc/[PID]/fd/..., and other systems may have their own non-portable equivalents).
What is happening in your case is that the read is failing (you're giving it a file descriptor which is not open), leaving your buffer with uninitialized garbage. Since you don't check the return value, you never know that it failed.
In pipe man page,
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.
The pipe is mainly used for the related process(parent and child). You are not able to use the pipe for non related process.
In related process, one end is closed. In other end process gets the SIGPIPE signal.
Example program using pipe :-
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
The above program creates a pipe, and then fork(2)s to create a child process; the child inherits a duplicate set of file descriptors that refer to the same pipe. After the fork(2), each process closes the descriptors that it doesn't need for the pipe (see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.
Note: For simplicity, I don't include much error checking and my sample code doesn't really serve any practical purpose.
What I want:
I want a program that fork()s a child process and has it invoke a process using execl(). My parent then retrieves the exit code for that process. This is fairly trivial.
What I tried:
int main(int argc, char** argv) {
int ch = fork();
if(ch == -1) {
perror(NULL);
}else if(ch == 0) {
// In child, invoke process
execl("/path/process", "process", 0);
}else {
// In parent, retrieve child exit code
int status = 0;
wait(&status);
// print exit status
if(WIFEXITED(status)) printf("%d\n", WEXITSTATUS(status));
}
}
My issue:
WEXITSTATUS() only retrieves the lower 8 bits of the exit value while I need all the bits from the int value. Specifically, process performs a calculation and the result may be larger than 8 bits. It may even be negative, in which case, the highest bit will be needed to represent the correct value.
What else I tried:
Also, while looking around, I found the pipe() function. However, I'm not sure how to use it in this situation since, after calling execl(), I can't write to a file descriptor from within the child.
So how can I go about retrieving a child's exit status that is larger than 8 bits?
I don't think that what you are trying to accomplish it's possible, because in Linux (actually i think it's UX specific), a process exit code is an 8bit number (max 256): 0-255 (and as a convention, 0 means success, anything else means error) and lots of stuff rely on this fact (including the macros you used). Take the following piece of code:
// a.c
int main() {
return 257;
}
If you compile it (gcc a.c), and run the resulting executable (a.out) checking (echo $?) its exit code (that will be truncated by the OS; hmm or is it the shell?) it will output 1 (wrap around arithmetic): 257 % 256 = 1.
As an alternative as you mentioned, you could use pipe (this post is pretty descriptive) or sockets (AF_UNIX type).
this code is from: How to send a simple string between two programs using pipes?
writer.c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char * myfifo = "/tmp/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;
}
reader.c
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
close(fd);
return 0;
}
the code, probably the parent/reader, should delete the fifo node, perhaps by calling rm.
Otherwise the fifo entry is left in existence, even across a re-boot, just like any other file.
I have created a FIFO, wrote to it and unlinked it.
To my surprise I was able to read data from the fifo after unlinking, why is that?
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_BUF 256
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
int pid = fork();
if (pid != 0)
{
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hi", sizeof("Hi"));
close(fd);
/* remove the FIFO */
unlink(myfifo);
}
else
{
wait(NULL);
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
close(fd);
return 0;
}
return 0;
}
Unless you pass the O_NONBLOCK flag to open(2), opening a FIFO blocks until the other end is opened. From man 7 fifo:
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.
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.
Which is to say, your parent / child processes are implicitly synchronized upon opening the FIFO. So by the time the parent process calls unlink(2), the child process opened the FIFO long ago. So the child will always find the FIFO object and open it before the parent calls unlink(2) on it.
A note about unlink(2): unlink(2) simply deletes the filename from the filesystem; as long as there is at least one process with the file (FIFO in this case) open, the underlying object will persist. Only after that process terminates or closes the file descriptor will the operating system free the associated resources. FWIW, this is irrelevant in the scope of this question, but it seems worth noting.
A couple of other (unrelated) remarks:
Don't call wait(2) on the child. It will return an error (which you promptly ignore), because the child didn't fork any process.
mkfifo(3), fork(2), open(2), read(2), write(2), close(2) and unlink(2) can all fail and return -1. You should gracefully handle possible errors instead of ignoring them. A common strategy for these toy programs is to print a descriptive error message with perror(3) and terminate.
If you just want parent to child communication, use a pipe: it's easier to setup, you don't need to unlink it, and it is not exposed in the filesystem (but you need to create it with pipe(2) before forking, so that the child can access it).
I want to learn how Linux pipes work! I wrote a small and easy program that use a pipe to communicate a string between parent and child process. However, the program results in a dead lock that I have not understood what is its cause.
Here is the code :
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SIZE 100
int
main(int argc, char *argv[])
{
int pfd[2];
int read_pipe=0, write_pipe=0;
pid_t cpid;
char buf[SIZE];
/* PIPE ***************************************
* pipe() creates a pair of file descriptors, *
* pointing to a pipe inode, and places them *
* in the array pointed to by filedes. *
* filedes[0] is for reading, *
* filedes[1] is for writing *
**********************************************/
if (pipe(pfd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
read_pipe=pfd[0];
write_pipe=pfd[1];
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
char * hello = "I am a child process\n";
sleep(1);
// wait until there is some data in the pipe
while (read(read_pipe, buf, SIZE) > 0);
printf("Parent process has written : %s\n", buf);
write(write_pipe, hello, strlen(hello));
close(write_pipe);
close(read_pipe);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
char * hello = "I am a parent process\n";
write(write_pipe, hello, strlen(hello));
while (read(read_pipe, buf, SIZE) > 0);
printf("Child process has written : %s\n", buf);
close(write_pipe);
close(read_pipe);
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
In this link you'll find the proper mannipulation of PIPEs between parent and child. Your problem here is that the communication is not being correctly set-up.
The PIPE should be used to communicate in only one direction, so one process has to close the read descriptor and the other has to close the write descriptor. Otherwise what will happen is that the call to 'read'(both on the father and the son), since it can detect that there is another process with an open write descriptor on the PIPE, will block when it finds that the PIPE is empty (not return 0), until someone writes something in it. So, both your father and your son are getting blocked on their respective read.
There are two solutions to this:
.You create two PIPEs, one for the communication in each direction, and perform the initialization as explained in the link above. Here you have to remember to close the write descriptor when you are done sending the message, so the other process' read will return, or condition the loop to the count of bytes read (not to the return of read), so you won't perform another call when you read the whole message. For example:
int bread = 0;
while(bread < desired_count)
{
bread += read(read_pipe, buf + bread, SIZE - bread);
}
.You create one PIPE as you did, and modify the flags on the read descriptor, using fcntl to also have O_NONBLOCK, so the calls to read won't block when there's no information in the PIPE. Here you need to check on the return value of the read to know you received something, and go adding up until you get the full length of the message. Also you will have find a way to synchronize the two processes so they won't read messages that are not meant for them. I don't recommend you to use this option, but you can try it if you want using condition variables.
Maybe you can tell if you see any of yout printf() outputs?
Anyway, if you want to establish a two way communication between your paent and child, yout should use two pipes, one for writing data form parent to child an the other for writing from child to parent. Furthermore, your read loops may be dangerous: if the data comes in two or more chunks the second read() overwrites the first portion (I've never seen tha happen with local pipes, but for example with sockets). And of course, yout is not automatically null terminated after read(), so just printing int with "%s" may also cause problems.
I hope that gives you some ideas to try.