I was looking at how to use pipe(2) on the man pages, and I don't understand a line in the source code they provided.
int
main(int argc, char *argv[])
{
int pipefd[2]; //Isn't this undefined??? so pipe(pipefd) would throw an error?
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);
}
}
I thought non-static function variables are just whatever is in memory? Why does this source code work?
pipefd array serves as output argument here, so there is no need to initialize it. pipe function writes into it.
Array pipefd returns 2 fds that refer to the end of pipes. Here, the pipefd[1] refers to the write end of the pipe and the pipefd[0] refers to the read end of the pipe.
In the above program:
The pipefd[1] is not used in child and hence it is closed. Data is read from pipefd[0] by child.
Note that the read blocks if pipe is empty and the write blocks if pipe is full.
The pipefd[0] is not used in parent and hence it is closed. Data is written into pipefd[1] by parent.
Also note that as pipe is unidirectional, the data written to the write end (pipefd[1]) of pipe is buffered by kernel until it is read from read end of the pipe(pipefd[0]).
Related
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
int main(void){
//Variables, p[2] for each end of the pipe. nbytes to read pipe return value SUCCESS or FAILURE. pid_t to hold pid of fork process.
// buffer to hold response from the child process.
int p[2], nbytes;
pid_t childpid;
char string[] = "Hello, World!\n";
char buffer[80];
//Declaration of pipe
pipe(p);
//Error handling.
if(((childpid = fork()) == -1) || (pipe(p) == -1))
{
perror("fork");
exit(1);
}
//Child process sends message to paprent.
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(p[0]);
/* Send "string" through the output side of pipe */
write(p[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(p[1]);
/* Read in a string from the pipe */
nbytes = read(p[0], buffer, sizeof(buffer));
printf("Received string: %s", buffer);
}
return(0);
}
Output > Received string: #�=zJ
The point of the exercise is to have a child process send a message through a pipe to the parent process and the parent returns the result. This exact code worked the first time I ran it, but then when I tried to run it a second time it started to return seemingly random characters each time. I tried to copy my buffer to another variable but then it was empty. Is the pipe actually not function the way I think it is? What am I doing wrong?
You first create a pipe with pipe(p); and then you create another with ... || (pipe(p) == -1)) Is that deliberate?
2nd Pipe was causing an issue.
You have:
pipe(p);
//Error handling.
if(((childpid = fork()) == -1) || (pipe(p) == -1))
{
perror("fork");
exit(1);
}
This creates two pipes — one in the line pipe(p); and the second in the condition if(((childpid = fork()) == -1) || (pipe(p) == -1)). This is wasteful at best. Moreover, the second pipe is after the fork(), so the parent and child processes don't access the same pipe any more — you overwrote the one created before the fork() which they do share. Test the result of pipe() before calling fork() and remove the extra condition in the if test:
if (pipe(p) != 0)
{
perror("pipe");
exit(1);
}
if ((childpid = fork()) < 0)
{
perror("fork");
exit(1);
}
Get used to testing for errors and writing appropriate code to handle them. It will be a major part of your life as a C programmer.
Later on in the code, you have:
{
/* Parent process closes up output side of pipe */
close(p[1]);
/* Read in a string from the pipe */
nbytes = read(p[0], buffer, sizeof(buffer));
printf("Received string: %s", buffer);
}
You need to heed the value of nbytes. Since it is an int, you could use:
printf("Received %d bytes: [%.*s]\n", nbytes, nbytes, buffer);
This limits the output to what was read, and reports 0 if that's what it gets. I suppose you should also check for -1 in nbytes before using it in the printf() statement:
if (nbytes < 0)
{
fprintf(stderr, "failed to read from pipe descriptor %d\n", p[0]);
// Or perror("read");
// Should you exit here with a non-zero status?
}
else
printf("Received %d bytes: [%.*s]\n", nbytes, nbytes, buffer);
Note: errors are reported on stderr; perror() does that automatically.
The problem is that you create two pipes when you really only need to check the first for errors:
// Declaration of pipe
if(pipe(p) == -1) { // check for error here
perror("pipe");
exit(1);
}
// Error handling.
if((childpid = fork()) == -1) { // and don't create another pipe here
perror("fork");
exit(1);
}
You should also check the return values from write and read. They may not write or read the full string in one go.
I am trying to understand why my program hangs. The Parent sends input froma
file it reads to the child program, and the child program will send the result of its computation back to it's parent. However, I have trouble sending the message back through a second pipe. The parent seems to hang when reading from the pipe.
From the other posts, I have read it seems to indicate that the parent should wait for the child to finish by using wait or waitpid (which in my case both of them does not resolve my issue).
I have notice by adding print statement that neither the PARENT or the CHILD finishes.. Could someone please explain to me why this is happening?
Why does this not work?
int main(int argc,char** argv) {
char buffer[1];
int i;
int fd1[2]; int fd2[2];
pipe(fd1); pipe(fd2);
pid_t pid;
// FIRST PROCESS.
// -------------------
pid = fork();
if(pid == 0) {
int cnt;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while(read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "( %s )", buffer);
cnt = cnt + *buffer - 48;
}
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
// PARENT.
// ------------------------
int file = open(argv[1], O_RDONLY);
// READ THE FILE.
while(read(file, buffer, 1) > 0) {
if (48 <= *buffer && *buffer <= 57) {
// PIPE TO CHILD.
write(fd1[1], buffer, 1);
}
}
// WAIT FOR CHILD TO FINISH SENDING BACK.
// int status = 0;
// waitpid(pid, &status, 0);
// THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.
// **** THIS IS THE PART WHERE IT DOESN'T WORK.
while(read(fd2[0], buffer, 1) > 0) {
fprintf(stderr, "RESULT : %s", buffer);
}
// CLOSING PIPES
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
close(file);
exit(0);
}
You aren't closing enough file descriptors in the parent soon enough.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
Now, your child process is following the RoT perfectly. But the corollary for parent processes is that they need to close the unused ends of the pipe, and they must close the write end of a pipe that they use to signal EOF to the reading end of that pipe. This is where your code fails.
Arguably, before reading the file, the parent process should close the read end of the pipe it uses to write to the child, and it should close the write end of the pipe it uses to read from the child.
Then, after reading the whole of the file, it should close the write end of the pipe to the child, before going into the 'read from child' loop. That loop never terminates because the parent still has the write end of the pipe open, so there's a process that could (but won't) write to the pipe.
Also, since the child writes the bytes of an integer onto a pipe, the parent should read the bytes of an integer. Using char buffer[1]; with a %s format is pointless; you need a null terminator for the string, and a single char buffer can't hold both a null byte and any data.
Along with various other improvements ('0' instead of 48, for example), you might end up with:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd1[2];
int fd2[2];
char buffer[1];
pipe(fd1);
pipe(fd2);
pid_t pid = fork();
if (pid == 0) {
int cnt = 0;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (int i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "(%c)", buffer[0]); // Changed
cnt = cnt + buffer[0] - '0';
}
putc('\n', stderr); // Aesthetics
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
int file = open(argv[1], O_RDONLY);
if (file < 0) {
fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
close(fd1[0]); // Added
close(fd2[1]); // Added
while (read(file, buffer, sizeof(buffer)) > 0) {
if ('0' <= buffer[0] && buffer[0] <= '9') {
write(fd1[1], buffer, sizeof(buffer));
}
}
close(file); // Moved
close(fd1[1]); // Added
// Rewritten
int result;
while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
fprintf(stderr, "RESULT : %d\n", result);
}
close(fd2[0]); // Added
// Close loop removed
return 0;
}
If that is stored in file pipe71.c and compiled, I get the following outputs when it is run:
$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$
I am in a scenario where I have to write a program that creates two processes. The father process
opens a text file for reading. It is assumed that the file is composed of words formed of
alphabetic characters separated by spaces. The child process enters a word on the keyboard. The
father process looks for the word in the file, and passes to the child the value 1 if the word is in
the file, and 0 otherwise. The son displays the result.
Here I think used the tubes for communication between these processes.
However, this communication seems to me difficult.
Does this ordering:
process father-process son-process father-process son is possible?
Attaching a simple pipe program for basic understanding. One can modify according to her needs.
/* simple_pipe.c
Simple demonstration of the use of a pipe to communicate
between a parent and a child process.
Usage: simple_pipe "string"
The program creates a pipe, and then calls fork() to create a child process.
After the fork(), the parent writes the string given on the command line
to the pipe, and the child uses a loop to read data from the pipe and
print it on standard output.
*/
#include <sys/wait.h>
#include "tlpi_hdr.h"
#define BUF_SIZE 10
int
main(int argc, char *argv[])
{
int pfd[2]; /* Pipe file descriptors */
char buf[BUF_SIZE];
ssize_t numRead;
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s string\n", argv[0]);
if (pipe(pfd) == -1) /* Create the pipe */
errExit("pipe");
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Child - reads from pipe */
if (close(pfd[1]) == -1) /* Write end is unused */
errExit("close - child");
for (;;) { /* Read data from pipe, echo on stdout */
numRead = read(pfd[0], buf, BUF_SIZE);
if (numRead == -1)
errExit("read");
if (numRead == 0)
break; /* End-of-file */
if (write(STDOUT_FILENO, buf, numRead) != numRead)
fatal("child - partial/failed write");
}
write(STDOUT_FILENO, "\n", 1);
if (close(pfd[0]) == -1)
errExit("close");
_exit(EXIT_SUCCESS);
default: /* Parent - writes to pipe */
if (close(pfd[0]) == -1) /* Read end is unused */
errExit("close - parent");
if (write(pfd[1], argv[1], strlen(argv[1])) != strlen(argv[1]))
fatal("parent - partial/failed write");
if (close(pfd[1]) == -1) /* Child will see EOF */
errExit("close");
wait(NULL); /* Wait for child to finish */
exit(EXIT_SUCCESS);
}
}
Have a look on more examples here on Chapter-44 The Linux Programming Interface.
Keep this book on your table or e-book on you computer, it's a bible for Linux programming.
I've been trying to write a really simple program in which the parent process passes 100 lines to a child process through a pipe. The child should then use the generated lines and execute the command line program more over those lines.
However, when I try to run the program, it just freezes. I was careful to close all descriptors not being used by both processes but I don't really understand what may be causing it.
Code:
int main(void){
int fd[2];
if (pipe(fd) == -1){
perror("Error creating pipe");
return 1;
}
dup2(fd[1], STDOUT_FILENO);
int i;
for (i = 1; i <= 100; i++){
printf("Line %d\n", i);
}
close(fd[1]);
pid_t pid = fork();
if(pid == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execlp("more", "more",(char*) NULL);
fprintf(stderr, "Failed to execute 'more'\n");
exit(1);
}
wait(NULL);
return 0;
}
I was careful to close all descriptors not being used by both processes
Not really.
dup2(fd[1], STDOUT_FILENO);
Here you make stdout a copy of fd[1].
close(fd[1]);
Here you close fd[1], but stdout is still open.
Then you fork. At this point both processes have access to the write end of the pipe via stdout.
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
In the child process you copy fd[0] to stdin and close fd[0].
Then, when you exec more, it still has access to both ends of the pipe (via stdin / stdout).
At the same time your parent process has access to both ends of the pipe (via fd[0] / stdout).
In effect you've closed nothing.
There's a second issue: Your parent process writes to stdout, which is bound to the write end of the pipe, without anyone reading it. Depending on how much you write, whether stdout is line buffered or block buffered, how big the stdout buffer is, and how much your pipe itself can store, this itself can deadlock. If the pipe runs full and there's no one around to read from it, printf will just block.
To fix this, don't dup2 in the parent process and don't write to the pipe before the child process has started.
int main(void){
int fd[2];
if (pipe(fd) == -1){
perror("Error creating pipe");
return 1;
}
pid_t pid = fork();
if (pid == -1) {
perror("Error spawning process");
return 2;
}
if (pid == 0) {
close(fd[1]); /* close write end of the pipe in the child */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execlp("more", "more", (char*)NULL);
fprintf(stderr, "Failed to execute 'more'\n");
exit(1);
}
close(fd[0]); /* close read end of the pipe in the parent */
FILE *fp = fdopen(fd[1], "w");
if (!fp) {
perror("Error opening file handle");
return 3;
}
for (int i = 1; i <= 100; i++){
fprintf(fp, "Line %d\n", i);
}
fclose(fp); /* flush and close write end of the pipe in the parent */
wait(NULL);
return 0;
}
I create a function exec_in_child which takes the command arguments, pipe file descriptors (fds), read_flag and write_flag as input. When write_flag is set to 1, the child process should duplicate stdout to fds[1], and then execute the command. When read_flag is set to 1, the child should duplicate the stdin to fds[0] and the execute the command.
Do I have to close one end of the pipe when I'm reading/writing to
the other end?
The code below doesn't work. I'm trying to execute /bin/ls inside a child process, write the stdout to the pipe, and then read
it off in the parent process and print it. I'm not able to read in
the parent process.
Can I read and write to the pipe inside the same process without closing other? This situation arises when I want to child to read
from pipe, execute, and then write to the pipe.
#include <stdio.h> /* printf */
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */
int exec_in_child(char *arguments[], const int temp[], int , int);
int main()
{
ssize_t bytes_read;
char *curr_dir = (char *)malloc(500);
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char *arguments[] = {"/bin/pwd",0};
exec_in_child(arguments, pipefd, 0, 1);
bytes_read = read(pipefd[0], curr_dir, strlen(curr_dir));
printf("%s = %d\n", "bytes read from pipe" ,(int)bytes_read);
printf("%s: %s\n","character read from the pipe",curr_dir);
return 0;
}
int exec_in_child(char * arguments[], const int fds[], int read_flag, int write_flag) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Error: Fork Failed");
}
else if (pid == 0){ /*inside the child process */
if (read_flag == 1) {
dup2(fds[0], 0);
perror("Dup2 stdin");
}
if (write_flag == 1) {
dup2(fds[1], 1);
perror("Dup2 stdout");
}
execv(arguments[0], arguments);
perror("Error in child");
exit(1);
} /* if (pid == 0) */
else {
while(pid != wait(0));
} /* if(pid < 0) */
return 0;
}
I get this result:
hmwk1-skk2142(test) > ./a.out
Dup2 stdout: Success
bytes read from pipe = 0
character read from the pipe:
To answer your questions:
1) You do not need to close either end of the pipe in order to use the other end. However, you generally want to close any end(s) of the pipe you're not using. The biggest reason to do this is that the pipe will only close when all open write file descriptors are closed.
2) Your code isn't working because you're using strlen() improperly. This function calculates the length of a string by searching for the null (0) character. When you malloc() the storage for curr_dir you have no guarantee of what resides there (though it will usually be zeroed, as in this case).
Thus, your call strlen(curr_dir) returns zero, and the read() system call thinks you want to read up to zero bytes of data. Change your read call to the following:
bytes_read = read(pipefd[0], curr_dir, 500);
and your code will work perfectly.
3) You can read and write to any pipe you've got a valid file descriptor to. A single process can absolutely read and write the same pipe.