Scenario: Say I have 8 files that I want to sort all the data of numbers it has in order from least to greatest. Only leaf proceses can sort all the numbers that a file contains. These leaf processes must send the sorted data to a parent process via pipes. This parent process will compare the data is receives and send which ever number is smaller to the next process up. It will do this until all number in the pipe is empty.
So think of it as a tree. We have one master process. With 8 files to sort, the Master process will spawn 2 processes off of it (a left and a right). Those two new processes will spawn their own processes. This will happen until there are 8 leaf processes at the bottom. Internal nodes can only hold onto one number. These will pass their number along a series of pipes until they reach the master process. The master process will output its piped contents to a file.
I've included the code here (as it is a bit lengthy but straightforward).
This works if I have 2 files to sort. So we have 1 master process and then two children. The two children sort their file's numbers and then pass them up. The master process then prints out the data in order from the pipes. However if I add some complexity (4 files), the leaf processes still send their data up, however when the master process begins to read the from the internal nodes pipes, it thinks it is empty and finishes the program without any data.
Any idea why the master process is thinking that its left and right pipes are empty?
Like I said, works great when there is one parent and 2 children. Anymore processes and it fails. (assuming that processing will happen in powers of 2).
NOTE: perror is being used for debugging purposes.
full program here [very messy as I have been doing a lot with it but it will compile.
The updated code in Pastebin is not a compilable function - let alone the complete program. That makes it hard to say what's really wrong.
However, one of the problems is in the code fragment:
if (pipe(upPipe) < 0 || pipe(leftPipe) < 0 || pipe(rightPipe) < 0)
...error exit...
if ((leftPID = fork()) < 0)
...error exit...
if(leftPID == 0){
fMax = ((fMax)/2);
dup2(leftPipe[WRITE], upPipe[WRITE]);
pipe(leftPipe);
pipe(rightPipe);
The call to dup2() is odd; you carefully map the write channel of the left pipe to the write channel of the up pipe.
The two pipe() calls after the dup2() fairly promptly screw up everything in the left child, opening 4 more file descriptors but losing the previous values stored in leftPipe and rightPipe.
You need to make your problem statement clearer. I cannot fathom from what you've got what you're supposed to have. There's a call to convertToInt() which takes no arguments and returns no value; what on earth is that doing? There's a call to freeMem(); it is not clear what that does.
z.c:42: error: ‘numberChar’ undeclared (first use in this function)
z.c:42: error: ‘sizeNumbers’ undeclared (first use in this function)
z.c:43: warning: implicit declaration of function ‘readFile’
z.c:43: error: ‘fileNames’ undeclared (first use in this function)
z.c:45: warning: implicit declaration of function ‘convertToInt’
z.c:46: error: ‘i’ undeclared (first use in this function)
z.c:46: error: ‘numbs’ undeclared (first use in this function)
z.c:47: error: ‘numbers’ undeclared (first use in this function)
z.c:48: warning: implicit declaration of function ‘freeMem’
Sorry, your question is unanswerable because you are not giving us:
The accurate requirements.
The code you've actually got compiling.
Your code does not have a good clean break up of the functions. Do you use a VCS (version control system - such as git)? If not, you should. I made the changed version below - which is essentially a complete rewrite - in 9 check-ins, and should probably have made more smaller check-ins than that. But using a VCS was crucial to me; it allowed me to make changes with confidence, knowing I would not lose anything valuable. And I didn't have to comment code out; I removed the stuff I didn't want. The solution below is 261 lines; the original was about 687 lines in total, including a lot of commented out code; when I'd finished stripping out the comments, etc, it came down to 469 lines.
When I got your code running (and reporting on which files were being opened by each child), I found that there were 2 processes opening each of files 2 and 3 (and since the data files didn't exist at the time, they failed at that point).
The revised code has an almost clean structure; the odd bit is the 'convertToString()' phase which reads binary integers off a pipe and converts them to ASCII output again. It works; I'm not convinced it is elegant. Instead of using an array of hard-coded file names, it takes an arbitrary list of file names from the command line (it does not have to be 8; it has been tested with 0 through 8, and I've no reason to think it won't handle 20 or more). I did a fair amount of testing with:
./piped-merge-sort [1-8]
There is copious diagnostic output. I've used two functions that I find vastly helpful in my work - I have them packaged up with some other related code in a more complex package, but the simple versions of err_error() and err_remark() functions really help me. Note that these versions report the PID of the reporting process for each call. They're also careful to pre-format the message into a string and then write the string in one print to standard error; otherwise, I was getting a lot of interleaved output which was confusing at best.
'Nuff said - here's the code:
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
/* Error reporting */
static void err_vremark(char *fmt, va_list args)
{
char buffer[256];
int errnum = errno;
int buflen = snprintf(buffer, sizeof(buffer), "%d: ", (int)getpid());
buflen += vsnprintf(buffer + buflen, sizeof(buffer) - buflen, fmt, args);
if (errnum != 0)
buflen += snprintf(buffer + buflen, sizeof(buffer) - buflen,
": errno = %d (%s)", errnum, strerror(errnum));
fprintf(stderr, "%s\n", buffer);
}
static void err_error(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vremark(fmt, args);
va_end(args);
exit(1);
}
static void err_remark(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vremark(fmt, args);
va_end(args);
}
enum { READ = 0 };
enum { WRITE = 1 };
enum { BUFFER_SIZE = 256 };
static int *a_data = 0;
static int a_used = 0;
static int a_size = 0;
void readFile(char const *fileName);
void freeMem(void);
void sortArray(void);
int intcmp(void const *n1, void const *n2);
static void sortMergeFiles(int fd, int number, char **names);
static void sortOneFile(int fd, const char *file);
static void convertToString(int fd, FILE *fp);
int main(int argc, char **argv)
{
int m_pipe[2];
pid_t pid;
if (pipe(m_pipe) < 0)
err_error("Failed to create master pipe");
if ((pid = fork()) < 0)
err_error("Failed to fork master");
else if (pid == 0)
{
close(m_pipe[READ]);
sortMergeFiles(m_pipe[WRITE], argc - 1, &argv[1]);
close(m_pipe[WRITE]);
}
else
{
close(m_pipe[WRITE]);
convertToString(m_pipe[READ], stdout);
close(m_pipe[READ]);
}
return 0;
}
static void convertToString(int fd, FILE *fp)
{
int value;
while (read(fd, &value, sizeof(int)) == sizeof(int))
fprintf(fp, "%d\n", value);
}
static int readInteger(int fd, int *value)
{
if (read(fd, value, sizeof(int)) != sizeof(int))
return EOF;
return 0;
}
static void writeInteger(int fd, int value)
{
if (write(fd, &value, sizeof(int)) != sizeof(int))
err_error("Failed to write integer to fd %d", fd);
}
static void mergeFiles(int fd_in1, int fd_in2, int fd_out)
{
int value_1;
int value_2;
int eof_1 = readInteger(fd_in1, &value_1);
int eof_2 = readInteger(fd_in2, &value_2);
while (eof_1 != EOF && eof_2 != EOF)
{
err_remark("v1: %d; v2: %d", value_1, value_2);
if (value_1 <= value_2)
{
writeInteger(fd_out, value_1);
eof_1 = readInteger(fd_in1, &value_1);
}
else
{
writeInteger(fd_out, value_2);
eof_2 = readInteger(fd_in2, &value_2);
}
}
while (eof_1 != EOF)
{
err_remark("v1: %d", value_1);
writeInteger(fd_out, value_1);
eof_1 = readInteger(fd_in1, &value_1);
}
while (eof_2 != EOF)
{
err_remark("v2: %d", value_2);
writeInteger(fd_out, value_2);
eof_2 = readInteger(fd_in2, &value_2);
}
}
static void sortMergeFiles(int fd, int number, char **names)
{
assert(number >= 0);
if (number == 0)
return;
else if (number == 1)
sortOneFile(fd, names[0]);
else
{
err_remark("Non-Leaf: processing %d files (%s .. %s)", number, names[0], names[number-1]);
int mid = number / 2;
int l_pipe[2];
int r_pipe[2];
pid_t l_pid;
pid_t r_pid;
if (pipe(l_pipe) < 0 || pipe(r_pipe) < 0)
err_error("Failed to create pipes");
if ((l_pid = fork()) < 0)
err_error("Failed to fork left child");
else if (l_pid == 0)
{
close(l_pipe[READ]);
close(r_pipe[READ]);
close(r_pipe[WRITE]);
sortMergeFiles(l_pipe[WRITE], mid, names);
close(l_pipe[WRITE]);
exit(0);
}
else if ((r_pid = fork()) < 0)
err_error("Failed to fork right child");
else if (r_pid == 0)
{
close(r_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
sortMergeFiles(r_pipe[WRITE], number - mid, names + mid);
close(r_pipe[WRITE]);
exit(0);
}
else
{
close(l_pipe[WRITE]);
close(r_pipe[WRITE]);
mergeFiles(l_pipe[READ], r_pipe[READ], fd);
close(l_pipe[READ]);
close(r_pipe[READ]);
err_remark("Non-Leaf: finished %d files (%s .. %s)", number, names[0], names[number-1]);
}
}
}
static void addNumberToArray(int number)
{
assert(a_used >= 0 && a_used <= a_size);
if (a_used == a_size)
{
int n_size = (a_size + 1) * 2;
int *n_data = realloc(a_data, sizeof(*n_data) * n_size);
if (n_data == 0)
err_error("Failed to allocate space for %d numbers", n_size);
a_data = n_data;
a_size = n_size;
}
a_data[a_used++] = number;
}
/* Could be compressed to write(fd, a_data, a_used * sizeof(int)); */
/* Arguably should check for write errors - but not SIGPIPE */
static void writeArray(int fd)
{
for (int i = 0; i < a_used; i++)
{
err_remark("Write: %d", a_data[i]);
write(fd, &a_data[i], sizeof(int));
}
}
void readFile(char const *fileName)
{
char buffer[BUFFER_SIZE];
FILE *fp;
fp = fopen(fileName, "r");
if (fp == NULL)
err_error("Failed to open file %s for reading", fileName);
while (fgets(buffer, sizeof(buffer), fp) != NULL)
{
char *nl = strchr(buffer, '\n');
if (nl != 0)
*nl = '\0';
err_remark("Line: %s", buffer);
addNumberToArray(atoi(buffer));
}
fclose(fp);
}
int intcmp(const void *n1, const void *n2)
{
const int num1 = *(const int *) n1;
const int num2 = *(const int *) n2;
return (num1 < num2) ? -1 : (num1 > num2);
}
void sortArray(void)
{
qsort(a_data, a_used, sizeof(int), intcmp);
}
void freeMem(void)
{
free(a_data);
}
static void sortOneFile(int fd, const char *file)
{
err_remark("Leaf: processing file %s", file);
readFile(file);
sortArray();
writeArray(fd);
freeMem();
err_remark("Leaf: finished file %s", file);
}
Related
I need the main prog to get two strings from the user and an argument for the other program, call fork() and then in child process I need to write the strings into pipe and send them to the other program which returns an int which I want to pass to parent so I'm trying to use another pipe for it but every time it stops right after inserting the strings.
So the main program: (EDITED)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char *cmpstr[] = {"lexcmp", "lencmp"};
int veclen = sizeof(cmpstr)/sizeof(char *);
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index;
int pid[2];
int pfd[4][2];
for(int i = 0; i < 4; i++)
{
if(pipe(pfd[i]) < 0)
{
perror("pipe");
return -2;
}
}
pid[0] = fork();
if(pid[0] == 0) // child a
{
close(pfd[0][1]);
close(pfd[2][0]);
dup2(pfd[0][0], STDIN_FILENO);
dup2(pfd[2][1], STDOUT_FILENO);
char *myargs[3];
myargs[0] = "./loopcmp";
myargs[1] = "lexcmp";
myargs[2] = NULL;
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
close(pfd[0][0]);
close(pfd[2][1]);
}
else
{
pid[1] = fork();
if(pid[1] == 0) //child b
{
close(pfd[1][1]);
close(pfd[3][0]);
dup2(pfd[1][0], STDIN_FILENO);
dup2(pfd[3][1], STDOUT_FILENO);
char *myargs[3];
myargs[0] = "./loopcmp";
myargs[1] = "lencmp";
myargs[2] = NULL;
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
close(pfd[1][0]);
close(pfd[3][1]);
}
else // parent
{
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
do {
printf("Please choose:\n");
for (int i=0 ; i < veclen ; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= veclen));
close(pfd[index][0]);
if(write(pfd[index][1], str1, strlen(str1)) == -1)
{
perror("writeToPipe");
return -2;
}
if(write(pfd[index][1], str2, strlen(str2)) == -1)
{
perror("writeToPipe");
return -2;
}
if(index == 0)
{
close(pfd[2][1]);
char rbuf[1];
while(read(pfd[2][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
}
if(index == 1)
{
close(pfd[3][1]);
char rbuf[1];
while(read(pfd[3][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
}
}
}
}
return 0;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n'); /* get to eol */
return retval;
}
int mygeti()
{
int ch;
int retval=0;
while(isspace(ch=getchar()));
while(isdigit(ch))
{
retval = retval * 10 + ch - '0';
ch = getchar();
}
while (ch != '\n')
ch = getchar();
return retval;
}
The other program - loopcmp: (Here I shouldn't change anything)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINELEN (80)
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int main(int argc, char *argv[])
{
int(*cmpfunc)(const char *, const char *) = NULL;
char str1[LINELEN + 1];
char str2[LINELEN + 1];
if (argc != 2)
return -1;
if (!strcmp(argv[1], "lexcmp"))
cmpfunc = lexcmp;
else if (!strcmp(argv[1], "lencmp"))
cmpfunc = lencmp;
else
return -1;
while (1)
{
if (mygets(str1, LINELEN) == NULL)
break;
if (mygets(str2, LINELEN) == NULL)
break;
printf("%d\n", cmpfunc(str1, str2));
fflush(stdout);
}
return 0;
}
int lencmp(const char *str1, const char *str2)
{
int val;
val = strlen(str1) - strlen(str2);
if (val < 0)
return 1;
if (val > 0)
return 2;
return 0;
}
int lexcmp(const char *str1, const char *str2)
{
int val;
val = strcmp(str1, str2);
if (val < 0)
return 1;
if (val > 0)
return 2;
return 0;
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval) while (getchar() != '\n'); /* get to eol */
return retval;
}
This is what I get:
Picture
and what I actually need it to print the interger returned from the exec of the child and then start again and get new two strings and so on till the user exits. what am I doing wrong? I can only modify the main program (the first one)
The first thing to do is ensure you are closing all unnecessary file descriptors in each process.
This means anything relating to the lexcmp child process should be closed in the lencmp child process, and vice versa. The parent needs to close the read ends of both "TO" pipes, and the write end of both "FROM" pipes.
Each of these closures should happen exactly once, where appropriate.
As is, in the parent, you are calling close(pfd[index][0]);, close(pfd[2][1]);, and close(pfd[3][1]); in a loop.
After calling dup2, you should immediately close the first argument (the original pipe end). As is, in the the children, you are attempting to close them after execvp is called, which leads into the next issue...
If execvp succeeds, it NEVER returns, as it will completely replace the process image. Anything expected to run after it is really operating in a failure state. So
if(execvp(myargs[0], myargs) == -1)
{
perror("exec");
return -2;
}
could be written as
execvp(myargs[0], myargs)
perror("exec");
return -2;
to the same effect.
Aside: the large if .. else if .. else structure of main is a bit hard to read, and not needed since the body of each if statement results in the child processes being replaced, or exiting on error.
The next issues have to do with deadlocking, which most typically occurs when two intercommunicating processes attempt blocking reads from one another at the same time.
Your child processes expect input in a very specific way: 2 lines at a time, creating a pair of strings. The two write calls, in the form of,
write(pfd[index][1], strX, strlen(strX))
do not write any newlines, thus the children wait forever, never to send any data back, and the parent will wait forever, never receiving any data.
Aside: mygets is severely flawed, in a few ways, including being unable to detect EOF or I/O failures (this function is a SIGSEGV in waiting). One of the more obnoxious failings is that the comment here
if (buf[strlen(buf) - 1] == 10) /* trim \r */
is just plain wrong. ASCII decimal 10 is '\n', the line feed, or newline character. '\r', or carriage return, would be decimal 13. This is why using character constants 'A' instead of integer constants 65 is highly encouraged.
The side effect here, generally speaking, is your strings are stripped of a trailing newline character.
The second deadlock occurs when you go to read the child process' response.
Firstly, this example
char rbuf[1];
while(read(pfd[N][0], &rbuf, 1) > 0)
{
write(STDOUT_FILENO, &rbuf, 1);
}
is malformed. Either remove the & operators, OR change char rbuf[1]; to char rbuf;. Fixing this, and the newline problem from above, will result in the parent process reading data back from the child.
The problem then becomes that a while (read(...) > 0) loop will continuously block execution of the calling process, waiting for more data to be available.
This means another deadlock when the child process has already moved on to trying to read another pair of lines from the parent process.
A simple solution is to attempt a single, reasonably large read in the parent, and rely on the behaviour of fflush(stdout); in the child to flush the pipe to the parent.
Here is a functional -ish example, with minimal changes made. This program still has some problems, such as: the parent process generally has no idea of the status of the child processes, and relying signal propagation (^C) from the terminal to end the process tree gracefully, since loopcmp does not handle EOF (should really discuss this with whoever wrote loopcmp.c / mygets).
Additionally, mygeti is flawed as well, as an invalid input cannot be distinguished from a valid input of 0. It also does not handle EOF, or prevent signed integer overflow.
Some more robust abstraction (functions and structures) around creating child processes would help a lot to clean this up further.
This should help you progress, though.
#define _POSIX_C_SOURCE 200809L
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define LINELEN (80)
char *mygets(char *buf, int len);
int mygeti();
void close_pipe(int fd[2])
{
close(fd[0]);
close(fd[1]);
}
int main(void)
{
char *cmpstr[] = {"lexcmp", "lencmp"};
int veclen = sizeof(cmpstr)/sizeof(char *);
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index;
int pid[2];
int pfd[4][2];
/* pfd[0] is TO lexcmp
* pfd[1] is TO lencmp
* pfd[2] is FROM lexcmp
* pfd[3] is FROM lencmp
*/
for(int i = 0; i < 4; i++)
if(pipe(pfd[i]) < 0) {
perror("pipe");
return -2;
}
pid[0] = fork();
if (pid[0] == 0) {
/* child lexcmp */
close_pipe(pfd[1]);
close_pipe(pfd[3]);
close(pfd[0][1]);
close(pfd[2][0]);
dup2(pfd[0][0], STDIN_FILENO);
dup2(pfd[2][1], STDOUT_FILENO);
close(pfd[0][0]);
close(pfd[2][1]);
char *args[] = { "./loopcmp", "lexcmp", NULL };
execvp(*args, args);
perror("exec");
return -2; /* This only returns from the child */
}
pid[1] = fork();
if (pid[1] == 0) {
/* child lencmp */
close_pipe(pfd[0]);
close_pipe(pfd[2]);
close(pfd[1][1]);
close(pfd[3][0]);
dup2(pfd[1][0], STDIN_FILENO);
dup2(pfd[3][1], STDOUT_FILENO);
close(pfd[1][0]);
close(pfd[3][1]);
char *args[] = { "./loopcmp", "lencmp", NULL };
execvp(*args, args);
perror("exec");
return -2; /* This only returns from the child */
}
/* parent */
close(pfd[0][0]);
close(pfd[1][0]);
close(pfd[2][1]);
close(pfd[3][1]);
while (1) {
printf("Please enter first string: ");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string: ");
if (mygets(str2, LINELEN) == NULL)
break;
do {
printf("Please choose (");
for (int i=0 ; i < veclen ; i++)
printf(" [%d] %s", i, cmpstr[i]);
printf(" ): ");
index = mygeti();
} while ((index < 0) || (index >= veclen));
if (0 >= dprintf(pfd[index][1], "%s\n%s\n", str1, str2)) {
fprintf(stderr, "Failed to write to child %d\n", index);
perror("dprintf");
return -2;
}
char buf[64];
ssize_t bytes = read(pfd[index + 2][0], buf, sizeof buf - 1);
if (-1 == bytes) {
perror("read from child");
return -2;
}
buf[bytes] = 0;
printf("Result: %s", buf);
}
}
char *mygets(char *buf, int len)
{
char *retval;
retval = fgets(buf, len, stdin);
buf[len] = '\0';
if (buf[strlen(buf) - 1] == 10) /* trim \r */
buf[strlen(buf) - 1] = '\0';
else if (retval)
while (getchar() != '\n'); /* get to eol */
return retval;
}
int mygeti()
{
int ch;
int retval=0;
while(isspace(ch=getchar()));
while(isdigit(ch))
{
retval = retval * 10 + ch - '0';
ch = getchar();
}
while (ch != '\n')
ch = getchar();
return retval;
}
Note the use of dprintf. If not available for whatever reason, just make sure to write a single newline after each string.
Final aside: with the way fgets works, the + 1 to the string buffer sizes are rather meaningless (although they are indirectly required here due to mygets performing its own, poorly designed buf[len] = '\0'). fgets writes at most len - 1 non-null bytes, always leaving room for the null terminating byte, which it places.
I'm currently learning and practicing c, but the exercise I'm doing wants each functions to have 25 lines limit (without changing { } or using single-line if statements!)
Please help if there's a way to make this even shorter.
void ft_write_file(void)
{
char c;
int fd;
int i;
i = 0;
if ((fd = open("write_exam", O_WRONLY | O_TRUNC | O_CREAT, 00777)) == -1)
{
ft_putstr("map error");
return ;
}
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
ft_allocate_g_var(i, c, 0);
i++;
}
int j = 0;
while (j < g_line)
{
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
}
j++;
}
close(fd);
}
To start with, don't try to do two things in one function. And try to write your functions with sensible arguments instead of hard-coding their subjects.
For example, your function is really doing two things:
Finding and potentially creating the output file (with a hard-coded name).
Copying the entire contents of one stream (hard-coded to stdin) to another stream.
So you could break that down: (prototypes only)
/* Returns fd or -1 on error */
int open_output(const char* name);
/* Returns number of bytes copied or -1 on error */
ssize_t copy_fd(int fd_dest, int fd_source);
Then your driver could be:
ssize_t copy_stdin_to_file(const char *name)
{
int fd = open_output(name);
if (fd < 0)
{
ft_putstr("Could not open output file");
return -1;
}
ssize_t copied = copy_fd(fd, 0);
if (copied < 0) {
ft_putstr("Could not write data.");
return copied;
}
}
A simple way would be to declare all the variables at the top in one line, for exemple :
char c; int fd; int i; i = 0;
Except from that I dont know, hope it can help a bit a least !
I have a program that reads from a Random Access File and is to return the smallest and largest number in the file. One requirement is that this is done with 4 processes using fork() and piping the results. I divide the file up into 4 chunks and have each process evaluate a chunk of the file. I find the max and min of each chunk and write them to a pipe. At the end I will compare the piped values and find the largest and smallest of the values.
I am having trouble reading from the pipes as they are returning -1. Any insight on what I am doing wrong? Thanks!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int findMin(int start, int end, const char * filename);
int findMax(int start, int end, const char * filename);
//Calculates minimum and maximum of a number
int main(int argc, char * argv[])
{
const char * filename; // name of file to read
FILE * ft; // file handle for the file
int pid, // process id of this process
num, // the number of integer values in the file
i, // loop control variable for reading values
temp=0; // used to store each value read from the file
long size; // size in bytes of the input file
/*********************************************************************/
filename = argv[1]; // read the file named on the command line
ft= fopen(filename, "rb");
if (ft)
{
pid = getpid();
fseek (ft,0,SEEK_END); //go to end of file
size = ftell(ft); //what byte in file am I at?
fseek (ft,0,SEEK_SET); //go to beginning of file
num = (int)size / (int)sizeof(int); // number of integer values
printf("file size: %li bytes\n", size);
printf("sizeof(int) = %i bytes\n",(int) sizeof(int));
printf("how many integers = %i\n\n", num);
fclose(ft);
}
//Split file size into quarters to make 4 processes
int increment = num/4;
int num1 = increment;
int num2 = num1 + increment;
int num3 = num2 + increment;
int num4 = num;
int status;
int pid1 = -1;
int pid2 = -1;
//Pipes
int fdmin1[2];
int fdmax1[2];
int fdmin2[2];
int fdmax2[2];
int fdmin3[2];
int fdmax3[2];
int fdmin4[2];
int fdmax4[2];
//initializing pipes
if(pipe(fdmin1) == -1)
{
perror("Piping fd1 failed");
return 0;
}
if(pipe(fdmax1) == -1)
{
perror("Piping fd2 failed");
return 0;
}
if(pipe(fdmin2) == -1)
{
perror("Piping fd3 failed");
return 0;
}
if(pipe(fdmax2) == -1)
{
perror("Piping fd4 failed");
return 0;
}
if(pipe(fdmin3) == -1)
{
perror("Piping fd3 failed");
return 0;
}
if(pipe(fdmax3) == -1)
{
perror("Piping fd4 failed");
return 0;
}
if(pipe(fdmin4) == -1)
{
perror("Piping fd3 failed");
return 0;
}
if(pipe(fdmax4) == -1)
{
perror("Piping fd4 failed");
return 0;
}
//temp variables for pipes
int temp1;
int temp2;
int temp3;
int temp4;
int temp5;
int temp6;
int temp7;
int temp8;
pid1 = fork();
printf("pid1: %d \n", pid1);
if(pid1 > 0)
{
//Process 1
temp1 = findMin(0, num1, filename);
temp2 = findMax(0, num1, filename);
close(fdmin1[0]);
if(write(fdmin1[1], &temp1, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmin1[1]);
close(fdmax1[0]);
if(write(fdmax1[1], &temp2, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmax1[1]);
}
else if(pid1 == 0)
{
//Process 2
temp3 = findMin(num1, num2, filename);
temp4 = findMax(num1, num2, filename);
close(fdmin2[0]);
if(write(fdmin2[1], &temp3, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmin2[1]);
close(fdmax2[0]);
if(write(fdmax2[1], &temp4, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmax2[1]);
pid2 = fork();
printf("pid2: %d \n", pid2);
if(pid2 > 0)
{
//Process 3
temp5 = findMin(num2, num3, filename);
temp6 = findMax(num2, num3, filename);
close(fdmin3[0]);
if(write(fdmin3[1], &temp5, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmin3[1]);
close(fdmax3[0]);
if(write(fdmax3[1], &temp6, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmax3[1]);
}
else if(pid2 == 0)
{
//Process 4
temp7 = findMin(num3, num4, filename);
temp8 = findMax(num3, num4, filename);
close(fdmin4[0]);
if(write(fdmin4[1], &temp7, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmin4[1]);
close(fdmax4[0]);
if(write(fdmax4[1], &temp8, sizeof(int)) == -1)
{
printf("Error writting to pipe");
}
close(fdmax4[1]);
}
}
//Close all pipe ends in all processes
close(fdmin1[0]);
close(fdmin1[1]);
close(fdmin2[0]);
close(fdmin2[1]);
close(fdmin3[0]);
close(fdmin3[1]);
close(fdmin4[0]);
close(fdmin4[1]);
close(fdmax1[0]);
close(fdmax1[1]);
close(fdmax2[0]);
close(fdmax2[1]);
close(fdmax3[0]);
close(fdmax3[1]);
close(fdmax4[0]);
close(fdmax4[1]);
//Wait for all processes to finish
int returnStatus;
waitpid(pid1, &returnStatus, 0);
int returnStatus2;
waitpid(pid2, &returnStatus2, 0);
//Make sure we are in parant process
if(pid1 > 0)
{
//Variables to compare min and max returned from processses
int min1;
int max1;
int min2;
int max2;
int min3;
int max3;
int min4;
int max4;
//read from pipe (error is occuring here)
close(fdmin1[1]);
if(read(fdmin1[0], &min1, sizeof(int)) == -1)
{
printf("Error reading");
}
close(fdmin1[0]);
printf("min1: %d \n", min1);
}
return 0;
}
//function to find the minimum in the file
int findMin(int start, int end, const char * filename)
{
int temp;
int smallestNum;
int i;
int length = end - start;
FILE * ft2;
ft2= fopen(filename, "rb");
fseek (ft2,start,SEEK_SET);
fread(&smallestNum,sizeof(int),1,ft2);
for(i = 0; i < length; i++)
{
fread(&temp,sizeof(int),1,ft2);
//printf("%d \n", temp);
if(temp < smallestNum)
{
smallestNum = temp;
}
/*
printf("%5i: %7i ",pid,temp);
if ((i+1)%5 == 0)
printf("\n");
*/
}
fclose(ft2);
printf("SmallestNum: %d \n", smallestNum);
return smallestNum;
}
//function to find maximum in file
int findMax(int start, int end, const char * filename)
{
int temp;
int largestNum;
int i;
int length = end - start;
FILE * ft3;
ft3= fopen(filename, "rb");
fseek (ft3,start,SEEK_SET);
fread(&largestNum,sizeof(int),1,ft3);
for(i = 0; i < length; i++)
{
fread(&temp,sizeof(int),1,ft3);
//printf("%d \n", temp);
if(temp > largestNum)
{
largestNum = temp;
}
/*
printf("%5i: %7i ",pid,temp);
if ((i+1)%5 == 0)
printf("\n");
*/
}
fclose(ft3);
printf("Largest Num: %d \n", largestNum);
return largestNum;
}
Here is the code for generating the Random Access File
/*
* This file generates a binary output file containing integers. It
* requires the output filename as a parameter and will take an
* argument indicating the number of values to generate as input.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define BIAS 0 // a bias value added to the numbers to "bias" the file
// contents to provide an offset to the min and max
int main(int argc, char * argv[]) {
const char * filename; // name of the output file
FILE * ft; // file handle for output file
int numtogen = 1000000; // default is to generate 1,000,000 numbers
int randomnum, i; // variables used in the loop generating numbers
if (argc<2) { // not enough arguments, need output file name
printf("Usage: gendata <filename> [number of numbers]\n");
return 1;
}
if (argc == 3) // optional third argument for number of numbers
numtogen = atoi(argv[2]);
filename=argv[1]; // use the filename entered to store numbers
srand(time(NULL)); // seed the random number generator
ft= fopen(filename, "wb") ;
if (ft) {
for (i = 0; i < numtogen; i++){
randomnum = rand() % numtogen + BIAS;
fwrite(&randomnum,sizeof(int),1,ft);
}
fclose(ft);
}
return 0;
}
I am having trouble reading from the pipes as they are returning -1. Any insight on what I am doing wrong? Thanks!
this is because in the main process you close two times the pipe, doing
printf("pid1: %d \n", pid1);
if(pid1 > 0)
{
...
close(fdmin1[0]); <<< HERE
and
//Close all pipe ends in all processes
close(fdmin1[0]); <<< HERE
so it is closed when you do :
if(read(fdmin1[0], &min1, sizeof(int)) == -1)
do not close fdmin1[0] before to read in but the reverse.
Note you also close two times fdmin1[1] and fdmax1[0] and fdmax1[1].
The usage of the pipes is very strange and probably no what you want :
fdmin1 is a pipe between the main process and itself, the main process does if(write(fdmin1[1], &temp1, sizeof(int)) == -1)and later if(read(fdmin1[0], &min1, sizeof(int)) == -1) so that pipe is useless and min1 is temp1
the main process does if(write(fdmax1[1], &temp2, sizeof(int)) == -1) but nobody read that value, that pipe is useless and temp2 = findMax(0, num1, filename); is done for nothing.
the main process child does if(write(fdmin2[1], &temp3, sizeof(int)) == -1) and if(write(fdmax2[1], &temp4, sizeof(int)) == -1) and if(write(fdmin3[1], &temp5, sizeof(int)) == -1) and if(write(fdmax3[1], &temp6, sizeof(int)) == -1) but nobody read, these four pipes are useless and all the min/max computing are done for nothing.
it is the same for the third created process doing if(write(fdmin4[1], &temp7, sizeof(int)) == -1) and if(write(fdmax4[1], &temp8, sizeof(int)) == -1) but nobody read, these two pipes are useless and the min/max computing are done for nothing.
That means at the end you do not get the right min/max value in the main process, but only the min value of the first quarter computing by the main process and all other computing are lost.
The code
//Wait for all processes to finish
int returnStatus;
waitpid(pid1, &returnStatus, 0);
int returnStatus2;
waitpid(pid2, &returnStatus2, 0);
is executed by all the child processes, because you do not exit or return when you have to do.
You also have an undefined behavior because you have a race condition between your processes, the execution is not the same depending on where I had usleep in your code. A parent process must wait for the end of its child process when needed, you do not at the right moment. Note your process numbering is wrong, there are only the main process and two children, so 3 processes rather than 4, //process4 does not exist and that comment is in process 2.
Except in the main process you do not read from the right position in the file because for findMin and findMax the parameter start correspond to a rank of int rather than a position in the file, you must replace
fseek (ft2,start,SEEK_SET);
fseek (ft3,start,SEEK_SET);
by
fseek (ft2,start*sizeof(int),SEEK_SET);
fseek (ft3,start*sizeof(int),SEEK_SET);
You also (try to) read one int too many doing
int length = end - start;
...
fread(&smallestNum,sizeof(int),1,ft2);
for(i = 0; i < length; i++)
{
fread(&temp,sizeof(int),1,ft2);
for instance replace the loop to have
for(i = 1; i < length; i++)
There also are a lot of useless variables in your program, if I compile with option -Wall :
bruno#bruno-XPS-8300:/tmp$ gcc -Wall -g p.c -o p
p.c: In function ‘main’:
p.c:250:16: warning: unused variable ‘max4’ [-Wunused-variable]
int max4;
^
p.c:249:16: warning: unused variable ‘min4’ [-Wunused-variable]
int min4;
^
p.c:248:16: warning: unused variable ‘max3’ [-Wunused-variable]
int max3;
^
p.c:247:16: warning: unused variable ‘min3’ [-Wunused-variable]
int min3;
^
p.c:246:16: warning: unused variable ‘max2’ [-Wunused-variable]
int max2;
^
p.c:245:16: warning: unused variable ‘min2’ [-Wunused-variable]
int min2;
^
p.c:244:16: warning: unused variable ‘max1’ [-Wunused-variable]
int max1;
^
p.c:48:12: warning: unused variable ‘status’ [-Wunused-variable]
int status;
^
p.c:20:8: warning: unused variable ‘temp’ [-Wunused-variable]
temp=0; // used to store each value read from the file
^
p.c:19:8: warning: unused variable ‘i’ [-Wunused-variable]
i, // loop control variable for reading values
^
p.c:17:8: warning: variable ‘pid’ set but not used [-Wunused-but-set-variable]
int pid, // process id of this process
^
bruno#bruno-XPS-8300:/tmp$
Out of that
You must check the value of argc before to do filename = argv[1];.
If fopen(filename, "rb"); fails you must stop the execution, currently you continue with an undefined behavior.
Note also your program can be simplified using array of pipe rather than separated variables for them, allowing you to use a loop rather than the sequence of if(pipe(fdmin1) == -1) ... if(pipe(fdmax4) == -1) .... It is the same to start the child processes, rather than to duplicate the code use a function to write it only one time. Doing that you can have a definition allowing any number of child process rather than dedicated to 4 only.
Going back to the statement
I divide the file up into 4 chunks and have each process evaluate a chunk of the file
This is an extreme case but you have to manage the case the file is too small to be divided by 4, this is not the case in your proposal.
this is done with 4 processes
Considering the main process is count among the 4, 3 children must be created. But rather than to have each child creating an other one if needed, it is more simple to have the 3 children created by the main process and the parallelism is a little better.
A program must be simple, I already said you have a lot of variables for nothing and lot of code is duplicated, also :
It is useless to have so many pipes, only one is enough to allow each child to send the min/max it computed because the pipe reads and writes are guaranteed to be atomic up to PIPE_BUF (larger than the size of 2 int)
It is useless to read the file so many times, you can search for the min and the max at the same time.
And finally a proposal :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define N 4 /* including the main process */
/* to send/receive result atomicaly through the pipe */
typedef struct {
int min, max;
} MinMax;
void findMinMax(long offset, long n, FILE * fp, MinMax * minmax);
//Calculates minimum and maximum of a number
int main(int argc, char * argv[])
{
const char * filename; // name of file to read
FILE * fp; // file handle for the file
long num; // the number of integer values in the file
long size; // size in bytes of the input file
long offset; // offset in file
int pp[2]; // the unique pipe
int pids[N-1];
MinMax minmax;
int i;
if (argc != 2) {
fprintf(stderr, "Usage: %s <filename>\n", *argv);
exit(-1);
}
filename = argv[1];
fp = fopen(filename, "rb");
if (fp == NULL) {
perror("cannot open file");
exit(-1);
}
/* get file size */
if (fseek(fp, 0, SEEK_END) == -1) { //go to end of file
perror("cannot fseek");
fclose(fp); /* also done automaticaly when exiting program */
exit(-1);
}
size = ftell(fp); //what byte in file am I at?
num = size / sizeof(int); // number of integer values
printf("file size: %li bytes\n", size);
printf("how many integers = %li\n\n", num);
if (num < N) {
fprintf(stderr, "the input file is too small, it must contains at least %i int\n", N);
fclose(fp); /* also done automaticaly when exiting program */
exit(-1);
}
//initializing pipe
if(pipe(pp) == -1) {
perror("Piping failed");
exit(-1);
}
offset = 0;
for (i = 0; i != N-1; ++i) {
pids[i] = fork();
switch (pids[i]) {
case 0:
/* child */
{
FILE * fp2 = fopen(filename, "rb");
if (fp2 == NULL) {
perror("child cannot open file");
exit(-1);
}
findMinMax(offset, num/N, fp2, &minmax);
printf("min max child %d : %d %d\n", i, minmax.min, minmax.max);
if (write(pp[1], &minmax, sizeof(minmax)) != sizeof(minmax)) {
perror("Error writting to pipe");
exit(-1);
}
}
exit(0);
case -1:
/* parent */
perror("Cannot fork");
exit(-1);
default:
/* parent, no error */
offset += (num/N)*sizeof(int);
}
}
findMinMax(offset, (size - offset)/sizeof(int), fp, &minmax);
printf("min max main : %d %d\n", minmax.min, minmax.max);
for (i = 0; i != N-1; ++i) {
int status;
MinMax mm;
if ((waitpid(pids[i], &status, 0) != -1) &&
(status == 0) &&
(read(pp[0], &mm, sizeof(mm)) == sizeof(mm))) {
if (mm.min < minmax.min)
minmax.min = mm.min;
if (mm.max > minmax.max)
minmax.max = mm.max;
}
else
fprintf(stderr, "cannot get result for child %d\n", i);
}
printf("global min max : %d %d\n", minmax.min, minmax.max);
return 0;
}
// function to find the minimum and maximum in the file
// n > 1
void findMinMax(long offset, long n, FILE * fp, MinMax * minmax)
{
int v;
if (fseek(fp, offset, SEEK_SET) == -1) {
perror("cannot fseek");
exit(-1);
}
if (fread(&minmax->min, sizeof(minmax->min), 1, fp) != 1) {
fclose(fp); /* also done automaticaly when exiting program */
perror("cannot read int");
exit(-1);
}
minmax->max = minmax->min;
while (--n) {
if (fread(&v, sizeof(v), 1, fp) != 1) {
fclose(fp); /* also done automaticaly when exiting program */
perror("cannot read int");
exit(-1);
}
if (v < minmax->min)
minmax->min = v;
if (v > minmax->max)
minmax->max = v;
}
fclose(fp); /* also done automaticaly when exiting program */
}
As you can see the code is much simple and I just have to modify #define N 4 to an other value to change the number of processes working in parallel.
Using your second program to generate 1000000 int in aze, compilation and execution of my proposal :
bruno#bruno-XPS-8300:/tmp$ gcc -g -Wall p.c
bruno#bruno-XPS-8300:/tmp$ ./a.out aze
file size: 4000000 bytes
how many integers = 1000000
min max main : 2 999995
min max child 0 : 10 999994
min max child 2 : 0 999998
min max child 1 : 3 999999
global min max : 0 999999
bruno#bruno-XPS-8300:/tmp$
Trying to copy the contents of a file to another file by copying n bytes at a time in c. I believe the code below works for copying one byte at a time but am not sure how to make it work for n number of bytes, have tried making a character array of size n and changing the read/write functions to read(sourceFile , &c, n) and write(destFile , &c, n), but the buffer doesn't appear to work that way.
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <time.h>
void File_Copy(int sourceFile, int destFile, int n){
char c;
while(read(sourceFile , &c, 1) != 0){
write(destFile , &c, 1);
}
}
int main(){
int fd, fd_destination;
fd = open("source_file.txt", O_RDONLY); //opening files to be read/created and written to
fd_destination = open("destination_file.txt", O_RDWR | O_CREAT);
clock_t begin = clock(); //starting clock to time the copying function
File_Copy(fd, fd_destination, 100); //copy function
clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; //timing display
return 0;
}
how to make it work for n number of bytes
Just read N number of bytes and copy that many bytes that you successfully read.
#define N 4096
void File_Copy(int sourceFile, int destFile, int n){
char c[N];
const size_t csize = sizeof(c)/sizeof(*c);
while (1) {
const ssize_t readed = read(sourceFile, c, csize);
if (readed <= 0) {
// nothing more to read
break;
}
// copy to destination that many bytes we read
const ssize_t written = write(destFile, c, readed);
if (written != readed) {
// we didn't transfer everything and destFile should be blocking
// handle error
abort();
}
}
}
You want to copy a buffer of size n at once:
void File_Copy(int sourceFile, int destFile, int n){
char c[n];
ssize_t st;
while((st = read(sourceFile , c, n)) > 0){
write(destFile , c, st);
}
}
Note, that not necessarily n bytes are always copied at once, it might be less. And you also have to check the return value of write() and handle the situation, when less bytes were written, as it fits your needs.
One example is a loop:
while (st > 0) {
int w = write(destFile, c, st);
if (w < 0) {
perror("write");
return;
}
st -= w;
}
Another issue: When you create the destination file here
fd_destination = open("destination_file.txt", O_RDWR | O_CREAT);
you do not specify the third mode parameter. This leads to a random mode, which might lead to this open() to fail the next time. So better add a valid mode, for example like this:
fd_destination = open("destination_file.txt", O_RDWR | O_CREAT, 0644);
This might have distorted your test results.
This is my version using lseek (no loop required):
It relies on read and write always processing the complete buffer and never a part of it (I don't know if this is guaranteed).
void File_Copy(int sourceFile, int destFile)
{
off_t s = lseek(sourceFile, 0, SEEK_END);
lseek(sourceFile, 0, SEEK_SET);
char* c = malloc(s);
if (read(sourceFile, c, s) == s)
write(destFile, c, s);
free(c);
}
The following code does not rely on this assumption and can also be used for file descriptors not supporting lseek.
void File_Copy(int sourceFile, int destFile, int n)
{
char* c = malloc(n);
while (1)
{
ssize_t readStatus = read(sourceFile, c, n);
if (readStatus == -1)
{
printf("error, read returned -1, errno: %d\n", errno);
return;
}
if (readStatus == 0)
break; // EOF
ssize_t bytesWritten = 0;
while (bytesWritten != readStatus)
{
ssize_t writeStatus = write(destFile, c + bytesWritten, readStatus - bytesWritten);
if (writeStatus == -1)
{
printf("error, write returned -1, errno is %d\n", errno);
return;
}
bytesWritten += writeStatus;
if (bytesWritten > readStatus) // should not be possible
{
printf("how did 'bytesWritten > readStatus' happen?");
return;
}
}
}
free(c);
}
On my system (PCIe SSD) I get best performance with a buffer between 1MB and 4MB (you can also use dd to find this size). Bigger buffers don't make sense. And you need big files (try 50GB) to see the effect.
I'm trying to write some code which uses pipes to communicate between a parent process and it's children. However, my pipe seems to give up after the first time I use it (that is, it stops working after the first use of the pipe). I'm not really sure how to fix this problem, and any help will be greatly appreciated. I also know that some of the coding practice I am using in this are not really ideal (mainly use of sleep).
const int READ = 0;
const int WRITE = 1;
char* COOP = "Criminal cooperates\n";
char* SIL = "Criminal doesn't talk\n";
char* reader(int);
void writer(int, char *c);
int main()
{
int c1pipe1[2];
int c1pipe2[2];
int c2pipe1[2];
int c2pipe2[2];
int c1sentence = 0;
int c2sentence = 0;
int r;
int c;
pipe(c1pipe1);
pipe(c1pipe2);
pipe(c2pipe1);
pipe(c2pipe2);
int C2;
int C1 = fork();
if(C1 > 0)
C2 = fork();
if(C1 < 0 || C2 < 0) //error
{
perror("fork() failed");
exit(1);
}
else if(C1 == 0)
{
close(c1pipe1[WRITE]);
close(c1pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C1 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c1pipe2[WRITE], "1");
else
writer(c1pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else if(C2 == 0)
{
close(c2pipe1[WRITE]);
close(c2pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C2 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c2pipe2[WRITE], "1");
else
writer(c2pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else //parent
{
int buff1; //stores choice of c1
int buff2; //stores choice of c2
close(c1pipe1[READ]);
close(c1pipe2[WRITE]);
close(c2pipe1[READ]);
close(c2pipe2[WRITE]);
for(c = 0; c< 10; c++)
{
buff1 = atoi(reader(c1pipe2[READ]));
buff2 = atoi(reader(c2pipe2[READ]));
printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1);
printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2);
if(buff1 && buff2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if(buff1 || buff2) // one cooperates, one is silent
{
if(buff1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
else if(!(buff1 && buff2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
sleep(1);
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
exit(0);
}
exit(0);
}
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
char* reader(int pipe_read_fd)
{
open(pipe_read_fd);
// Allocate buffer to store
// result of read
int buffer_size = 1024;
char buffer[buffer_size];
// Keep reading until we exhaust
// buffer or reach end of file
int i = 0;
while (i < buffer_size
&& read(pipe_read_fd, &buffer[i], 1) > 0)
{ i++; }
if (i < buffer_size) {
// Add null termination
buffer[i] = '\0';
} else {
// We exhausted buffer
fprintf(stderr, "Warning: buffer full.\n");
buffer[buffer_size-1] = '\0';
}
//printf("%s", buffer);
// Close the pipe
close(pipe_read_fd);
return buffer;
}
You need to close more of the pipes. The child processes must close every pipe file descriptor that they are not using. You have 8 pipe file descriptors; each child process has to close 6 of those - at least! You would be very well advised not to create all the pipes up front as you have done - it is complicated to control things and get all the right descriptors closed.
Looking at the code more closely, the parent does not write messages to the child processes, so you have twice as many pipes as you need - you only need one pipe for each child process to write back to the parent with.
You also do not open() already open file descriptors to the pipes...but how did you get the code to compile? You must be missing the correct header (#include <fcntl.h>) for open() and compiling without enough warning options enabled.
Your variables COOP and SIL are unused in the code presented.
Your writer() function not only mistakenly tries to open an already closed file descriptor, it also closes it, which means that there is no way to send back the extra messages after the first. You should only close the file descriptor once finished - after the loop in the main program for each child. This is why you only see one message.
It is also worth getting into the habit of error-checking the return from every system call that can fail. There are a few that can't fail - getpid() is one such. But I/O operations are notorious for failing for reasons outside the direct control of the program (or, in this case, within the control of the program), so you should check that writes succeed. When you get back an EBADF - bad file descriptor - error, you know something is up.
You have similar problems with close() (and open()) in reader(), plus the additional problem that you attempt to return a pointer to a local automatic variable - which is not a good idea, ever. Again, a decent compiler (like GCC) with warnings enabled will tell you about such things. I used this command to compile your program:
gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
pipe.c -o pipe
Your child processes are always going to generate the same sequence of (pseudo-)random numbers, which isn't very exciting. You should probably use something like:
srand(getpid());
to ensure they get different sequences.
Your reader() function is both not enthusiastic enough and too enthusiastic about reading the data. You read a single byte at a time, but you then loop to accumulate single bytes, so the code waits around for all 10 results to be known, and then spits everything out at once. Since a 32-bit integer can store a number up to 1,111,111,111 without problem, you would get just one number back from your call to atoi() on the first iteration, which isn't quite what you wanted.
Reads and writes on pipes are atomic - in the sense that if the writing process writes 6 bytes and the reading process attempts to read more than 6 bytes, then the packet of 6 bytes will be returned by a single read, even if there are other bytes in the pipe waiting to be read; those extra bytes will be returned on subsequent calls to read().
So, your reader() function should be passed in a buffer to use, along with its size; the code should attempt to read that buffer size; it should null terminate what it does receive; it can return the pointer to the buffer it was passed; it should error check the returned value from read().
The code for the two child processes is essentially the same - you should use an appropriately parameterized function rather than writing out the code twice.
Putting it all together, you end up with something like this (which works fine for me on MacOS X 10.6.6 with GCC 4.5.2):
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
const int READ = 0;
const int WRITE = 1;
static char* reader(int fd, char *buffer, size_t bufsiz);
static void writer(int fd, const char *c);
static void child_process(int *my_pipe, int *his_pipe);
static void err_exit(const char *fmt, ...)
{
va_list args;
int errnum = errno;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int c1pipe[2];
int c2pipe[2];
int c1sentence = 0;
int c2sentence = 0;
int c;
if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0)
err_exit("Failed to open a pipe\n");
int C2 = 0;
int C1 = fork();
if (C1 > 0)
C2 = fork();
if (C1 < 0 || C2 < 0) //error
err_exit("fork() failed\n");
else if (C1 == 0)
child_process(c1pipe, c2pipe);
else if (C2 == 0)
child_process(c2pipe, c1pipe);
else //parent
{
int choice1; //stores choice of c1
int choice2; //stores choice of c2
char buffer1[BUFSIZ];
char buffer2[BUFSIZ];
close(c1pipe[WRITE]);
close(c2pipe[WRITE]);
for (c = 0; c< 10; c++)
{
choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1)));
choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1)));
printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1);
printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2);
if (choice1 && choice2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if (!(choice1 && choice2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
else if (choice1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
}
return(0);
}
static void writer(int pipe_write_fd, const char *c)
{
int len = strlen(c);
if (write(pipe_write_fd, c, len) != len)
err_exit("Write failed\n");
}
static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz)
{
int i = read(pipe_read_fd, buffer, bufsiz-1);
if (i < 0)
err_exit("Read failed\n");
buffer[i] = '\0';
return buffer;
}
static void child_process(int *my_pipe, int *his_pipe)
{
int c;
srand(getpid());
close(my_pipe[READ]);
close(his_pipe[READ]);
close(his_pipe[WRITE]);
for (c = 0; c < 10; c++)
{
writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0");
sleep(1);
}
close(my_pipe[WRITE]);
}
Note how the error routine captures errno early - to avoid damaging it. It is one of the perils of using global variables; they may change when you call a function. Don't use them when you can avoid them (but note that you can't avoid using errno completely, in general).
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
I'm not sure which function open you are trying yo use but the usual one takes a filename and returns a file descriptor. In any case you are discarding the return value so I suppose that this doesn't matter.
What is clear is that you close the pipe immediately after the first write so it is "correct" that the next write will fail; the pipe has been closed.
If you fix this problem then the next problem is that reader will, one byte at a time, all of the available input - up to 1024 bytes - before closing the read pipe. As reader is called in a loop, the read attempt in the second iteration will fail.