My program have to send some bytes of information by using unnamed pipes.
I have a txt file named "input" which is supposed to be read by the program and it's information have to be send and write in another file named "output". Also i must use read(), write(), open() functions.
My code look like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#define BUFSIZE 25
int main( int argc, char *argv[] ) {
srand(time(NULL));
pid_t pid;
int mypipefd[2];
int ret;
char buf[BUFSIZE];
int output;
int stream;
int nbytes;
ret = pipe(mypipefd);
if( ret == -1 ) {
perror( "pipe error");
exit(1);
}
pid = fork();
if( pid == -1 ) {
perror( "FORK ERROR...");
exit(2);
}
if( pid == 0 ) {
/* CHILD */
printf(" Child process...\n");
stream = open("input.txt", O_RDONLY);
if (close(mypipefd[0]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(3);
}
while ( (nbytes = read(stream, buf, BUFSIZE)) > 0 ) {
sleep(rand() %2);
write(mypipefd[1], buf, nbytes );
}
if ( close(stream) == -1 ) {
perror("ERROR CLOSING STREAM");
exit(4);
}
}
else {
/* PARENT */
printf(" Parent process...\n");
output = open("output.txt", O_CREAT | O_WRONLY, 00777);
while ( (nbytes = read(mypipefd[0], buf, BUFSIZE)) > 0 ) {
write(output, buf, nbytes);
}
printf("buf: %s\n", buf);
if (close(output) == -1) {
perror("ERROR CLOSING OUTPUT");
exit(5);
}
if (close(mypipefd[1]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(6);
}
}
return 0;
}
Unfortunately the code is not working terminal screen
Before I tried while loop and was sending all the information at once, it worked, but output file looked like this output file
while the input file look like this input file
The primary bug was that the parent must do close(mypipefd[1]) before the parent read loop (and not after). This prevented the parent from seeing EOF on the pipe after the child was done writing.
Also, you were missing a waitpid in the parent.
The printf for buf in the parent was in the wrong place [after the read loop]. At that point, buf can't be guaranteed to have the correct data or that it's correctly zero-terminated. That's why stdout had some garbage chars at the end.
So, In addition to outputting to the output file, the loop should output to stdout, but should use fwrite as buf can't be guaranteed to be zero terminated.
I had missed that in my initial post, so I've corrected it.
As per my top comments, the child should loop on a [possible] partial write to the pipe. I coded that.
Here's the version with the bugs annotated and fixed:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 25
int main( int argc, char *argv[] ) {
srand(time(NULL));
pid_t pid;
int mypipefd[2];
int ret;
char buf[BUFSIZE];
int output;
int stream;
int nbytes;
ret = pipe(mypipefd);
if( ret == -1 ) {
perror( "pipe error");
exit(1);
}
pid = fork();
if( pid == -1 ) {
perror( "FORK ERROR...");
exit(2);
}
if( pid == 0 ) {
/* CHILD */
printf(" Child process...\n");
stream = open("input.txt", O_RDONLY);
if (close(mypipefd[0]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(3);
}
while ( (nbytes = read(stream, buf, BUFSIZE)) > 0 ) {
sleep(rand() %2);
#if 0
write(mypipefd[1], buf, nbytes );
#else
// NOTE: this _should_ work but adds extra at the end
int off;
int wlen;
for (off = 0; nbytes > 0; off += wlen, nbytes -= wlen) {
wlen = write(mypipefd[1], buf + off, nbytes );
if (wlen <= 0)
break;
}
#endif
}
if ( close(stream) == -1 ) {
perror("ERROR CLOSING STREAM");
exit(4);
}
// NOTE/FIX: child must close it's side of the pipe
#if 1
close(mypipefd[1]);
#endif
}
else {
/* PARENT */
printf(" Parent process...\n");
// NOTE/FIX: this must be closed _before_ the read loop -- holding it
// open prevents parent from seeing EOF on pipe
#if 1
if (close(mypipefd[1]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(6);
}
#endif
#if 1
printf("buf: ");
#endif
output = open("output.txt", O_CREAT | O_WRONLY, 00777);
while ( (nbytes = read(mypipefd[0], buf, BUFSIZE)) > 0 ) {
write(output, buf, nbytes);
#if 1
fwrite(buf,1,nbytes,stdout);
#endif
}
// NOTE/BUG: the buffer at this point will only have the data from
// the _last_ read and may not be null terminated
#if 0
printf("buf: %s\n", buf);
#else
printf("\n");
#endif
if (close(output) == -1) {
perror("ERROR CLOSING OUTPUT");
exit(5);
}
// NOTE/BUG: this must be closed _before_ the parent's read loop
#if 0
if (close(mypipefd[1]) == -1 ) {
perror("ERROR CLOSING PIPE");
exit(6);
}
#endif
// NOTE/FIX: this is missing (prevents orphan/zombie child process)
#if 1
waitpid(pid,NULL,0);
#endif
}
return 0;
}
UPDATE:
but i don't understand what does "for" loop do here
A write to a pipe can generate a "short write" (e.g. you want to write 20 but the return value (i.e. number of bytes actually written) comes back with 15. You have to index into the buffer and write the remaining bytes in subsequent writes.
There is a kernel limit on how many bytes can be written in a single atomic write (e.g.) if you did write(mypipefd[1],buf,10000000), the kernel doesn't have space allocated for such a large write, so it will return the value of what it could append to the pipe buffer [in the kernel].
Also, let's say the kernel pipe buffer can hold 64 bytes. And you write buffers of size 64 to it. Maybe the reader is reading only 32 bytes. So, the first write is fine. Then reader reads out 32 bytes. So, the next write to the pipe of 64, there is only space for 32 bytes, so the write will return 32
Program have to display: "buf: This is ra" then "buf: ndom text"
Okay, I've fixed that
At last, I need to implement error handling everywhere.
I've annotated places where I'd add error and handling, along with some things to look for.
Anyway, here's an updated version. I've left in the // NOTE/* comments but removed the #if/#endif pairs to make an easier read.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFSIZE 25
int
main(int argc, char *argv[])
{
srand(time(NULL));
pid_t pid;
int mypipefd[2];
int ret;
char buf[BUFSIZE];
int output;
int stream;
int nbytes;
ret = pipe(mypipefd);
if (ret == -1) {
perror("pipe error");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("FORK ERROR...");
exit(2);
}
if (pid == 0) {
/* CHILD */
printf(" Child process...\n");
stream = open("input.txt", O_RDONLY);
if (close(mypipefd[0]) == -1) {
perror("ERROR CLOSING PIPE");
exit(3);
}
while ((nbytes = read(stream, buf, BUFSIZE)) > 0) {
sleep(rand() % 2);
// NOTE/FIX: writing to pipes _can_ generate a _short_ write. that
// is, (e.g.) if the length given to write is 20, the return value
// may be only 15. this means that the remaining 5 bytes must be
// sent in a second/subsequent write
int off;
int wlen;
for (off = 0; nbytes > 0; off += wlen, nbytes -= wlen) {
wlen = write(mypipefd[1], buf + off, nbytes);
if (wlen < 0) {
perror("ERROR WRITING TO FILE");
exit(3);
}
if (wlen == 0)
break;
}
}
if (close(stream) == -1) {
perror("ERROR CLOSING STREAM");
exit(4);
}
// NOTE/FIX: child must close it's side of the pipe
// NOTE/ERRCODE: check error code here
close(mypipefd[1]);
}
else {
/* PARENT */
printf(" Parent process...\n");
// NOTE/FIX: this must be closed _before_ the read loop -- holding it
// open prevents parent from seeing EOF on pipe
if (close(mypipefd[1]) == -1) {
perror("ERROR CLOSING PIPE");
exit(6);
}
// NOTE/ERRCODE: this should be checked for -1 (i.e. output file
// could not be opened for file permission, etc. or other reasons
// similar to those for the file write below)
output = open("output.txt", O_CREAT | O_WRONLY, 00777);
// NOTE/FIX: we read one less than buffer size to allow for adding an
// artificial zero byte at the end
while ((nbytes = read(mypipefd[0], buf, BUFSIZE - 1)) > 0) {
// NOTE/ERRCODE: error handling _could_ be added here but it would
// be rare (e.g. filesystem has an I/O error because it's full or
// marked R/O because of an I/O error on the underlying disk)
write(output, buf, nbytes);
// write partial buffer to stdout
buf[nbytes] = 0;
printf("buf: %s\n",buf);
}
if (close(output) == -1) {
perror("ERROR CLOSING OUTPUT");
exit(5);
}
// NOTE/FIX: this is missing (prevents orphan/zombie child process)
// NOTE/ERRCODE: yes, this _can_ have an error return but here it's
// unlikely because we _know_ that pid is valid
// what can be done is to do:
// int status;
// waitpid(pid,&status,0)
// then process the return code from the child using the W* macros
// provided (e.g. WIFEXITED, WSTATUS) on status
waitpid(pid, NULL, 0);
}
return 0;
}
Related
I Implement a pipe that "transfers" from one process data file size of 100MB to another process.
The Whole Send from one process to the another works well, but it takes soo long... around 2.5+ minutes.
I want to change my functions to fread&fwrite to make the function faster instead reading one char each time how I can do it with the pd[0] & pd[1] but I hardly understand how I can change it
Any help will be appreciated!
HERE IS MY CODE:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int fd[2];
pid_t childpid;
char readbuffer[65535];
pipe(fd);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
FILE *fp2 = fopen("data.txt", "rb");
if (fp2 == NULL) {
//handle error here
return -1;
}
int c;
while ((c = getc(fp2)) != EOF) {
if ((write(fd[1], &c, 1)) < 1) {
fprintf(stderr, "Write to pipe failed.\n");
perror("write");
exit(EXIT_FAILURE);
}
}
} else {
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
char *new_data = "new_data.txt";
FILE *fp = fopen(new_data, "wb");
ssize_t num_bytes;
while (num_bytes = (read(fd[0], readbuffer, sizeof(readbuffer))) > 0) {
fwrite(readbuffer, 1, num_bytes, fp);
memset(readbuffer, 0, 65535);
}
fclose(fp);
}
return 0;
}
EDITED ROUND 2:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int fd[2];
pid_t childpid;
char readbuffer[1024];
pipe(fd);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
FILE *fp2 = fopen("data.txt", "rb");
if (fp2 == NULL) {
//handle error here
return -1;
}
int c;
// while ((c = getc(fp2)) != EOF) {
// if ((write(fd[1], &c, 1)) < 1) {
// fprintf(stderr, "Write to pipe failed.\n");
// perror("write");
// exit(EXIT_FAILURE);
// }
// }
ssize_t num_bytes;
while ((num_bytes = fread(readbuffer, sizeof(readbuffer), 1024,fp2)) > 0) {
fwrite(readbuffer, 1, num_bytes, fd[1]);
//memset(readbuffer, 0, 65535);
}
} else {
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
char *new_data = "new_data.txt";
FILE *fp = fopen(new_data, "wb");
ssize_t num_bytes;
while ((num_bytes = read(fd[0], readbuffer, sizeof(readbuffer))) > 0) {
fwrite(readbuffer, 1, num_bytes, fp);
//memset(readbuffer, 0, 65535);
}
fclose(fp);
}
return 0;
}
A few issues ...
In your original code, the main issue [speed wise] was using read or write with a length of 1.
Also, although a stream may compensate a bit for fgetc, it is still one byte at a time.
The solution I've come up with is to implement what William Pursell suggested: Use fdopen to attach a stdio stream (i.e. FILE *) to the pipe.
We can do this for both parent and child.
Then, just looping on fread/fwrite in both processes works.
Note that the parent and should do fclose.
And, the parent should wait for the child to complete (e.g. waitpid).
Here is the modified code:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#ifdef DEBUG
#define dbgprt(_fmt...) \
do { \
int sverr = errno; \
fprintf(stderr,_fmt); \
errno = sverr; \
} while (0)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
int
main(void)
{
int fd[2];
FILE *fpinp;
FILE *fpout;
pid_t childpid;
int status;
ssize_t rlen;
ssize_t wlen;
#if 0
char buffer[65535];
#else
char buffer[1024];
#endif
setlinebuf(stdout);
setlinebuf(stderr);
pipe(fd);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
fpinp = fopen("data.txt", "rb");
if (fpinp == NULL) {
perror("child/fopen");
exit(7);
}
fpout = fdopen(fd[1],"wb");
if (fpout == NULL) {
perror("child/fdopen");
exit(8);
}
while (1) {
rlen = fread(buffer,1,sizeof(buffer),fpinp);
dbgprt("child: LOOP rlen=%zd\n",rlen);
if (rlen < 0) {
perror("child/fread");
exit(9);
}
if (rlen <= 0)
break;
wlen = fwrite(buffer,1,rlen,fpout);
dbgprt("child: LOOP wlen=%zd\n",wlen);
if (wlen < 0) {
perror("child/fwrite");
exit(9);
}
}
fclose(fpinp);
fclose(fpout);
exit(0);
}
else {
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
char *new_data = "new_data.txt";
fpout = fopen(new_data, "wb");
if (fpout == NULL) {
perror("parent/fopen");
exit(3);
}
fpinp = fdopen(fd[0],"rb");
if (fpinp == NULL) {
perror("parent/fdopen");
exit(4);
}
while (1) {
rlen = fread(buffer, 1, sizeof(buffer), fpinp);
dbgprt("parent: LOOP rlen=%zd\n",rlen);
if (rlen < 0) {
perror("parent/fread");
exit(5);
}
if (rlen <= 0)
break;
wlen = fwrite(buffer, 1, rlen, fpout);
dbgprt("parent: LOOP wlen=%zd\n",wlen);
if (wlen < 0) {
perror("parent/fwrite");
exit(6);
}
}
fclose(fpinp);
fclose(fpout);
waitpid(childpid,&status,0);
dbgprt("status=%8.8X\n",status);
}
return 0;
}
I am writing a simple code to implement the indirect input function for a unix/linux shell.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
extern void error(char* message);
void
cisshRedirectedInput(char* command[], char* inputFile)
{
//Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
//For the child process
if ((pid=fork())==0)
{
//Try to input files, failing on an error
fd=open(inputFile,O_RDONLY);//To read input file
if(fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
//use dup() to copy file
close(1);
if(dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
//Close file and exec()
close(fd);
execvp(command[0], command);
//If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if(wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if(status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
However, after compilation (no error reported), but when I try (fileList created already)
sort -r <fileList
The shell just stuck there without giving me answer, what is the problem please?
The standard input file descriptor is 0 (or STDIN_FILENO), not 1 (or STDOUT_FILENO).
Either use:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
close(0);
if (dup(fd) < 0) …
close(fd);
Or:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
if (dup2(fd, 0) < 0) …
close(fd);
It is good that your code does the close(fd) after duplicating to a standard I/O descriptor — that is almost always correct. It's also good that you are checking that the key system calls succeed. (There isn't much you can do if close() fails.)
This simple modification of your code (key change: use close(0); instead of close(1);) works for me. Did you null terminate your argument list?
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void error(char *message)
{
fprintf(stderr, "%s\n", message);
}
void
cisshRedirectedInput(char *command[], char *inputFile);
void
cisshRedirectedInput(char *command[], char *inputFile)
{
// Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
// For the child process
if ((pid = fork()) == 0)
{
// Try to input files, failing on an error
fd = open(inputFile, O_RDONLY); // To read input file
if (fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
// use dup() to copy file
close(0);
if (dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
// Close file and exec()
close(fd);
execvp(command[0], command);
// If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if (wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if (status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
int main(void)
{
char *args[] = { "sort", "-r", 0 };
cisshRedirectedInput(args, "fileList");
return 0;
}
Input file:
bash-assoc-arrays.sh
cissh.c
fileList
kwargs.py
makefile
posixver.h
rangeinc.c
select.c
spc.py
testcsv.py
uncrustify.bug
yield.py
Output:
yield.py
uncrustify.bug
testcsv.py
spc.py
select.c
rangeinc.c
posixver.h
makefile
kwargs.py
fileList
cissh.c
bash-assoc-arrays.sh
In the code below, I am simply trying to send a file via stdin to a child process which will exec the cat OS command. The code compiles fine. Here is how I call it from the command line:
$ ./uniquify < words.txt
However, when I run it I get a seg fault error. I am really having a hard time understanding how the flow if information is supposed to work through pipes to children. I am trying to make the code as simple as possible, so I can understand it, but it is not yet making sense. Any help would be appreciated.
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NUM_CHILDREN 2
int main(int argc, char *argv[])
{
pid_t catPid;
int writeFds[NUM_CHILDREN];
int catFds[2];
int c = 0;
FILE *writeToChildren[NUM_CHILDREN];
//create a pipe
(void) pipe(catFds);
if ((catPid = fork()) < 0) {
perror("cat fork failed");
exit(1);
}
//this is the child case
if (catPid == 0) {
//close the write end of the pipe
close(catFds[1]);
//close stdin?
close(0);
//duplicate the read side of the pipe
dup(catFds[0]);
//exec cat
execl("/bin/cat", "cat", (char *) 0);
perror("***** exec of cat failed");
exit(20);
}
else { //this is the parent case
//close the read end of the pipe
close(catFds[0]);
int p[2];
//create a pipe
pipe(p);
writeToChildren[c] = fdopen(p[1], "w");
} //only the the parent continues from here
//close file descriptor so the cat child can exit
close(catFds[1]);
char words[NUM_CHILDREN][50];
//read through the input file two words at a time
while (fscanf(stdin, "%s %s", words[0], words[1]) != EOF) {
//loop twice passing one of the words to each rev child
for (c = 0; c < NUM_CHILDREN; c++) {
fprintf(writeToChildren[c], "%s\n", words[c]);
}
}
//close all FILEs and fds by sending and EOF
for (c = 0; c < NUM_CHILDREN; c++) {
fclose(writeToChildren[c]);
close(writeFds[c]);
}
int status = 0;
//wait on all children
for (c = 0; c < (NUM_CHILDREN + 1); c++) {
wait(&status);
}
return 0;
}
Since your question seems to be about understanding how pipes and forks work, I hope below programs can help you. Please notice that this is for illustration only. It wouldn't qualify for commercial implementation, but I wanted to keep it short!
You can compile the two programs as follows:
cc pipechild.c -o pipechild
cc pipeparent.c -o pipeparent
Then execute with ./pipeparent
pipeparent.c source
/* pipeparent.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MESSAGE "HELLO!\n"
#define INBUFSIZE 80
#define RD 0 // Read end of pipe
#define WR 1 // Write end of pipe
int main(void)
{
int ptocpipe[2]; // Parent-to-child pipe
int ctoppipe[2]; // Chile-to-parent pipe
pid_t childpid; // Process ID of child
char inbuf[80]; // Input from child
int rd; // read() return
int rdup; // dup():ed stdin for child
int wdup; // dup():ed stdout for child
char *eol; // End of line
// Create pipe for writing to child
if (pipe(ptocpipe) < 0) {
fprintf(stderr, "pipe(ptocpipe) failed!\n");
return 2;
}
// Create pipe for writing back to parent
if (pipe(ctoppipe) < 0) {
fprintf(stderr, "pipe(ctoppipe) failed!\n");
return 2;
}
// Verify that one of the pipes are working by filling it first
// in one end and then reading it from the other. The OS will
// buffer the contents for us. Note, this is not at all necessary,
// it's just to illustrate how it works!
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
read(ptocpipe[RD], inbuf, INBUFSIZE);
if (strlen(inbuf) != strlen(MESSAGE)) {
fprintf(stderr, "Failed to flush the toilet!\n");
return 6;
} else {
printf("Wrote to myself: %s", inbuf);
}
// Next, we want to launch some interactive program which
// replies with exactly one line to each line we send to it,
// until it gets tired and returns EOF to us.
// First, we must clone ourselves by using fork(). Then the
// child process must be replaced by the interactive program.
// Problem is: How do we cheat the program to read its stdin
// from us, and send its stdout back to us?
switch (childpid = fork()) {
case -1: // Error
fprintf(stderr, "Parent: fork() failed!\n");
return 3;
case 0: // Child process
// Close the ends we don't need. If not, we might
// write back to ourselves!
close(ptocpipe[WR]);
close(ctoppipe[RD]);
// Close stdin
close(0);
// Create a "new stdin", which WILL be 0 (zero)
if ((rdup = dup(ptocpipe[RD])) < 0) {
fprintf(stderr, "Failed dup(stdin)\n");
return 4;
}
// Close stdout
close(1);
// Create a "new stdout", which WILL be 1 (one)
if ((wdup = dup(ctoppipe[WR])) < 0) {
fprintf(stderr, "Failed dup(stdout)\n");
return 5;
}
// For debugging, verify stdin and stdout
fprintf(stderr, "rdup: %d, wdup %d\n", rdup, wdup);
// Overload current process by the interactive
// child process which we want to execute.
execlp("./pipechild", "pipechild", (char *) NULL);
// Getting here means we failed to launch the child
fprintf(stderr, "Parent: execl() failed!\n");
return 4;
}
// This code is executed by the parent only!
// Close the ends we don't need, to avoid writing back to ourself
close(ptocpipe[RD]);
close(ctoppipe[WR]);
// Write one line to the child and expect a reply, or EOF.
do {
write(ptocpipe[WR], MESSAGE, strlen(MESSAGE));
if ((rd = read(ctoppipe[RD], inbuf, INBUFSIZE)) > 0) {
// Chop off ending EOL
if ((eol = rindex(inbuf, '\n')) != NULL)
*eol = '\0';
printf("Parent: Read \"%s\" from child.\n", inbuf);
}
} while (rd > 0);
fprintf(stderr, "Parent: Child done!\n");
return 0;
}
pipechild.c source
/* pipechild.c
* Note - This is only for illustration purpose!
* To be stable, we should catch/ignore signals,
* and use select() to read.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#define MAXCOUNT 5 // Maximum input lines toread
#define INBUFSIZE 80 // Buffer size
int main(void)
{
char buff[INBUFSIZE];
int remains = MAXCOUNT;
pid_t mypid;
char *eol;
mypid = getpid(); // Process-ID
fprintf(stderr, "Child %d: Started!\n", mypid);
// For each line read, write one tostdout.
while (fgets(buff, INBUFSIZE, stdin) && remains--) {
// Chop off ending EOL
if ((eol = rindex(buff, '\n')) != NULL)
*eol = '\0';
// Debug to console
fprintf(stderr, "Child %d: I got %s. %d remains.\n",
mypid, buff, 1 + remains);
// Reply to parent
sprintf(buff, "Child %d: %d remains\n", mypid, 1 + remains);
write(1, buff, strlen(buff));
}
fprintf(stderr, "Child %d: I'm done!\n", mypid);
return 0;
}
I am having a problem with my assignment I have due for class. I have to create a read/write program that will read a text file into it and write the contents to a new text file. The thing is, I have to use parent/child processes and piping. I have to pass the contents into the pipe with one child, and use another child to read the data from the pipe and write it to a new file.
I have three files: parent.c, read.c and write.c. The program works fine for the most part! It even transfers the data from one file to the other perfectly. The problem I am having is that the write.c process will never complete. I think it may have something to do with the reading from pipe(won't return 0 or EOF). Here is my source code:
parent.c
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFF_SIZE 255
int main(int ac, char* av[])
{
if(ac <3)
{
printf("Please enter all required arguments!\n");
exit(0);
}
int pfd[2];
int pipeCreated;
char readFile[50];
char writePipe[20];
pid_t child_pid_read;
pid_t child_pid_write;
pipeCreated = pipe(pfd);
if(pipeCreated == -1)
{
printf("An error occurred when trying to create a pipe\n");
exit(0);
}
strcpy(readFile, av[1]);
sprintf(writePipe,"%d", pfd[1]);
child_pid_read = fork();
char writeFile[50];
char readPipe[20];
//Handling the read()
switch(child_pid_read)
{
//Error in case forfk() failed
case -1:
perror("fork failed");
return 1;
//Handle child processes
case 0:
if(close(pfd[0]) == -1)
{
printf("An error occurred while closing the pipe\n");
exit(0);
}
if(execle("./read.out", "./read.out", readFile, writePipe, (char*)0, NULL) == -1)
{
printf("Child: Error creating read.\n");
exit(0);
}
default:
wait(&child_pid_read);
strcpy(writeFile, av[2]);
sprintf(readPipe,"%d", pfd[0]);
child_pid_write = fork();
break;
}
//Handling the write
switch(child_pid_write)
{
//Error in case fork() failed
case -1:
perror("fork failed");
return 1;
//Handle child processes
case 0:
if(close(pfd[1]) == -1)
{
printf("An error occurred while closing the pipe\n");
exit(0);
}
if(execle("./write.out", "./write.out", writeFile, readPipe, (char*)0, NULL) == -1)
{
printf("Child: Error creating read.\n");
exit(-1);
}
break;
default:
wait(&child_pid_write);
break;
}
printf("Write completed!");
return 0;
}
read.c:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFF_SIZE 16
int main(int ac, char* av[])
{
char buffer[BUFF_SIZE];
int fd;
int pid;
if(ac > 1)
{
fd = open(av[1], O_RDONLY);
if(fd == -1)
{
printf("error: Could Not Open File\n");
exit(0);
}
pid = atoi(av[2]);
}
int num_read = 1;
while(1)
{
num_read = read(fd, buffer, BUFF_SIZE);
if(num_read == -1)
{
printf("Error reading file\n");
exit(0);
}
if(num_read == 0)
{
break;
}
if(write(pid, buffer, num_read) != num_read)
{
printf("Error writing to pipe\n");
break;
}
}
close(fd);
return 1;
}
write.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFF_SIZE 1
int main(int ac, char* av[])
{
char buffer[BUFF_SIZE];
int fd = open(av[1], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
int pid = atoi(av[2]);
int num_read = 1;
while(1)
{
num_read = read(pid, buffer, BUFF_SIZE);
printf("num_read: %d\n", num_read);
if(num_read == -1)
{
printf("Error reading pipe\n");
break;
}
if(write(fd, buffer, num_read) != num_read)
{
printf("Error writing to file\n");
break;
}
if(num_read == EOF)
{
break;
}
}
close(fd);
return 1;
}
Please look over my code and suggest corrections. I am passing in the names of the text files through the terminal (./parent.out, oldFile.txt, newFile.txt).
Two problems:
You're not forking the write process until after wait() for the read process returns. If the read process tries to write more data than will fit in the pipe buffer, it will block and never exit. You need to allow both processes to run concurrently to avoid this deadlock. It will work with a small file, but if the file is bigger than 4KB it will hang.
After forking the write process, the parent process has to close pfd[0]. The reader of a pipe doesn't get EOF until all processes that have the write end open close it. It should be:
default:
if(close(pfd[0]) == -1)
{
printf("An error occurred while closing the pipe\n");
exit(0);
}
wait(&child_pid_write);
break;
Your child want to read data,why you close the fd[0], return from pipe indicating that fd[0] for reading and fd[1] for writing.As i can't add a comment, i have to post the comment here....
fifo.3 source code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <time.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF //4096
#define TEN_MEG (1024 * 1024 * 1)
void* thread_tick(void* arg)
{
int count =0;
while(count < 4){
printf("hello, world!\n");
sleep(1);
count++;
}
}
void* thread_write(void* arg)
{
int pipe_fd;
int res;
int bytes_sent = 0;
char buffer[BUFFER_SIZE ];
int count=0;
if (access(FIFO_NAME, F_OK) == -1) {
res = mkfifo(FIFO_NAME, 0777);
if (res != 0) {
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
while(count < 10){
printf("write: Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_WRONLY);
printf("write: Process %d result %d \n", getpid(), pipe_fd);
if (pipe_fd != -1) {
while(bytes_sent < TEN_MEG) {
res = write(pipe_fd, buffer, BUFFER_SIZE);
if (res == -1) {
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("write: Process %d finished , count =%d\n", getpid(),count);
count++;
}
}
void CreateThread(void* (*start_routine)(void*), void* arg,int stacksize, int priority)
{
pthread_t app_thread;
pthread_attr_t thread_attr;
int res;
int max_priority;
int min_priority;
struct sched_param scheduling_value;
res = pthread_attr_init(&thread_attr);
if (res != 0) {
perror("Attribute creation failed\n");
exit(EXIT_FAILURE);
}
res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
if (res != 0) {
perror("Setting detached attribute failed");
exit(EXIT_FAILURE);
}
res = pthread_attr_setstacksize(&thread_attr, stacksize);
if (res != 0) {
perror("Set stack size failed\n");
exit(EXIT_FAILURE);
}
res = pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);
if (res != 0) {
perror("Setting schedpolicy failed");
exit(EXIT_FAILURE);
}
max_priority = sched_get_priority_max(SCHED_RR);
min_priority = sched_get_priority_min(SCHED_RR);
scheduling_value.sched_priority = priority;
res = pthread_attr_setschedparam(&thread_attr, &scheduling_value);
if (res != 0) {
perror("Setting schedpolicy failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&app_thread, &thread_attr, (*start_routine), arg);
if(res != 0){
perror("Thread creation failed\n");
exit(EXIT_FAILURE);
}
pthread_attr_destroy(&thread_attr);
//res = pthread_join(app_thread ,0 );
//return app_thread;
}
int main()
{
CreateThread(thread_write, 0, 50000, 99);
CreateThread(thread_tick, 0, 50000, 98);
// pthread_join(w,0 );
// pthread_join(t ,0 );
return 0;
}
fifo.4 source code :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF //4096
int main()
{
int pipe_fd;
int res;
char buffer[BUFFER_SIZE ];
int bytes_read = 0;
int count = 0;
memset(buffer, '\0', sizeof(buffer));
while(count < 10){
printf("read: Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY);
printf("read: Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1) {
do {
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
(void)close(pipe_fd);
}
else {
exit(EXIT_FAILURE);
}
printf("read: Process %d finished, %d bytes read , count =%d\n", getpid(), bytes_read,count);
count++;
}
return 0;
}
this is the first time I post code on Stack overflow, so it is in a mess.
Above are two C source code. fifo3.c has two thread and thread_write is to write data to named fifo.
fifo4.c is to read data from named fifo.
my question:
1) how does the read(pipe_fd, buffer, BUFFER_SIZE) behave when write() is writing data to fifo? If read() can not read data, SHOULD not read() return 0 and then exit, why read() would wait write() to finish write data??? of course, how does write() behave when read() is reading?
2) in fifo3.c , I create two threads, when I create them detached , the program can not run !!!
but joinable, they could run correctly !!I do not know why!
In theory, they both could function right.
Answer for Question-1:
If read cannot read data it will 'block' till data arrives, this is called blocking mode read. Incase of a blocking mode read, the read call blocks till a data arrives. If you wish to change it to non-blocking mode, you can use fcntl functionality, if the same is supported.
For other queries, it is best that you read about it through man pages as a concise answer will be difficult.
Answer for Question-2:
When you create a thread detached, it means the created threads are not bound to the parent thread which creates it. So, the parent thread will just exit, if it completes it's work. If the parent happens to be the main thread, then when it exits the process also will exit, which will cause program not to run.