Blocking read with closed file descriptor - c

I have three processes organized in a circular link: P1->P2->P3->P1. I have 3 unnamed pipes for each of the three links. Each process reads an integer from his neighbour on the left and writes an integer to the neighbour on the write. After some time an termination condition will be satisfied at one of the processes. This will make that process close his open file descriptors and terminate. However, the other processes still are able to write to the now closed fd and the reads are blocking. Have I misunderstood how unnamed pipes are really working?
void monitor(int *fdr, int *fdw, int pid) {
int A,B,ret,avg;
char send_buf[10], recv_buf[10];
srand((unsigned int) fdr);
A = 20;
close(fdr[1]); close(fdw[0]);
while(1) {
B = rand() % 30;
avg = (A+B)/2;
if (avg > 20) {
printf("P%d computes to high avg. Quit!\n",pid);
break;
}
sprintf(send_buf, "%d", avg);
printf("P%d will write %s\n",pid,send_buf);
ret = write(fdw[1],send_buf,strlen(send_buf)+1);
if (ret == 0) {
printf("P%d cant write. Quit\n",pid);
break;
}
printf("P%d successful write=%d\n", pid, ret);
ret = read(fdr[0], recv_buf,10);
if (ret == 0) {
printf("P%d cant read. Quit\n",pid);
break;
}
printf("P%d reads %s\n",pid,recv_buf);
A = atoi(recv_buf);
}
// Closing filedescriptors should also break the other
// processes out of the while loop
close(fdr[0]);
close(fdw[1]);
printf("P%d closed all descriptors\n",pid);
}

Related

Minibash in C, problem making pipes between execvp and parent proccess

I have to do this as a university project so I cant share the whole code, im sorry for that.
I have to create a function called "read" that enables the user to create new env variables, thats the easy part. The problem comes when I call that function as the last one of the commands array e.g "ls | grep aux.txt | read a" this should give the env var A the value aux.txt, the problem is that it get stuck in the
fgets(value, sizeof(value),stdin);
and I cant even recover the terminal.
Thanks in advance for the help if you need more info about the problem I will happily give it.
I can't reproduce exactly the main function as there are parts that are not mine but I hope this helps:
char **argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv) {
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++) {
pipe(fd[i]);
}
for(int i = 0; i< 3; i++){
pid = fork();
if(pid == 0){
if(i ==0){
dup2(fd[0][1], 1);
fun_close(fd);
execvp(argvv[0][0], argvv[0]);
}
if(i == 1){
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
fun_close(fd);
execvp(argvv[1][0], argvv[0]);
}
}else{
if(i == 2){
close(fd[0][1]);
close(fd[0][0]);
fun_read("read a", 3, fd[1]);
}
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
void fun_close(int **fd){
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
}
And here is the fun_read:
int fun_read(char **command, int argc, int fd[]){
char **env_varv;
char value[1024];
char last_var[1024];
long size = 0;
char *token;
int status;
char *delim = " \t\n";
env_varv = malloc((argc-1) * sizeof(char *));
for(int i = 1; i < argc; i++){
env_varv[i-1] = strdup(command[i]);
wait(status);
}
if (fd[0] !=0){
printf("%d\n", fd[0]);
dup2(fd[0],0);
close(fd[0]);
close(fd[1]);
}
fgets(value, sizeof(value),stdin);
int i = 0;
token = strtok(value, delim);
last_var[0] = '\0';
while(token != NULL){
if(i == argc-2){
while (token != NULL){
strcat(last_var,token);
setenv(env_varv[i],last_var,1);
token = strtok(NULL,delim);
strcat(last_var," ");
}
}
else if (env_varv[i] != NULL){
setenv(env_varv[i],token,1);
token = strtok(NULL,delim);
i++;
}
else{
break;
}
}
return 0;
The program should put an envariomental variable called a with the value of example.
postscript: it seems like there is no problem if the previous command is a builtin "echo hi | echo hi2 | read a" $a=hi2
Sincerely I have tried all, changing the pipes doesnt work, changing fgets for read doesn't help either. Is the only part of the code I haven't been able to fix
This fragment of code shows some problems:
char ***argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv) {
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++) {
pipe(fd[i]);
}
for(int i = 0; i< 3; i++){
pid = fork();
if(pid == 0){
if(i ==0){
close(fd[0][0]);
close(fd[1][1]);
close(fd[1][0]);
dup2(fd[0][1], 1);
execvp(argvv[0][0], argvv[0]);
}
if(i = 1){
close(fd[0][1]);
close(fd[1][0]);
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
execvp(argvv[1][0], argvv[0]);
}
if(i = 2){
close(fd[0][1]);
close(fd[0][0]);
close(fd[1][1]);
dup2(fd[1][0], 0);
fun_read("read a", 3, fd[1]);
}
}
}
Rule of Thumb
You aren't closing enough pipe file descriptors in any of the processes.
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 or F_DUPFD_CLOEXEC.
Other comments on the use of pipes
If the parent process will not communicate with any of its children via
the pipe, it must ensure that it closes both ends of the pipe early
enough (before waiting, for example) so that its children can receive
EOF indications on read (or get SIGPIPE signals or write errors on
write), rather than blocking indefinitely.
Even if the parent uses the pipe without using dup2(), it should
normally close at least one end of the pipe — it is extremely rare for
a program to read and write on both ends of a single pipe.
Note that the O_CLOEXEC option to
open(),
and the FD_CLOEXEC and F_DUPFD_CLOEXEC options to fcntl() can also factor
into this discussion.
If you use
posix_spawn()
and its extensive family of support functions (21 functions in total),
you will need to review how to close file descriptors in the spawned process
(posix_spawn_file_actions_addclose(),
etc.).
Note that using dup2(a, b) is safer than using close(b); dup(a);
for a variety of reasons.
One is that if you want to force the file descriptor to a larger than
usual number, dup2() is the only sensible way to do that.
Another is that if a is the same as b (e.g. both 0), then dup2()
handles it correctly (it doesn't close b before duplicating a)
whereas the separate close() and dup() fails horribly.
This is an unlikely, but not impossible, circumstance.
Analyzing your code
The parent process has the pipes open; if the commands are reading from the pipes, they won't get EOF until the parent process closes them. Although you close most of the pipes in the child processes, you don't close those that you duplicate to the standard I/O channels — and yet that is required too.
Note that if (i = 1) should be if (i == 1), and if (i = 2) should be if (i == 2). The first of those bugs prevents your fun_read() from being invoked — which is why it isn't responding. Using diagnostic printing to standard error would confirm that fun_read() is never called.
So, at bare minimum, you need to have code like this:
char ***argvv;
int fd[2][2];
int pid;
int main(int argc, char ***argvv)
{
argvv[0][0] = "echo";
argvv[0][1] = "elpmaxe";
argvv[1][0] = "rev";
argvv[2][0] = "read";
argvv[2][1] = "a";
for (int i = 0; i < 2; i++)
{
pipe(fd[i]);
}
for (int i = 0; i < 3; i++)
{
pid = fork();
if (pid == 0)
{
if (i == 0)
{
dup2(fd[0][1], 1);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
execvp(argvv[0][0], argvv[0]);
fprintf(stderr, "failed to execute %s\n", argvv[0][0]);
exit(EXIT_FAILURE);
}
if (i == 1)
{
dup2(fd[0][0], 0);
dup2(fd[1][1], 1);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
execvp(argvv[1][0], argvv[0]);
fprintf(stderr, "failed to execute %s\n", argvv[1][0]);
exit(EXIT_FAILURE);
}
if (i == 2)
{
dup2(fd[1][0], 0);
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
fun_read("read a", 3, fd[1]);
exit(EXIT_SUCCESS);
}
}
}
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
/* wait loop here - and not before */
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
Note that it is important to handle failure to execute. And error messages should be reported to standard error, not to standard output.
Given that the same sequence of 4 calls to close() is made 4 times, a function to do the job seems appropriate. You could make it:
static inline void close_pipes(int fd[2][2])
{
close(fd[0][0]);
close(fd[0][1]);
close(fd[1][0]);
close(fd[1][1]);
}
There is a decent chance the compiler will inline the function, but it is easier to see that the same 4 descriptors are closed if one function always does the closing. For bigger arrays of pipes (more processes), you'd have a loop inside the close_pipes() function with a counter as well as the array.
There are still some issues to be resolved, notably with the fun_read() function. The fd[1] file descriptors were both closed, so passing those to fun_read() doesn't seem likely to be useful. Since fun_read() is executed in a separate process, any changes made by fun_read() won't be reflected in the parent process. There are probably other problems too.
AFAICT, on looking at fun_read() more closely, the fd argument should not be needed at all. The paragraph of code:
if (fd[0] != 0) {
printf("%d\n", fd[0]);
dup2(fd[0], 0);
}
is not useful. You've already redirected standard input so it comes from the pipe and then closed the pipe file descriptor. This paragraph then changes standard input to come from the closed descriptor, which isn't going to help anything. But none of this helps you with the fact that anything done by fun_read() is done in a child process of your shell, so the environment in the main shell is not going to be affected.

C Pipe between a parent and 2 children

I can not figure out why only 1 child sends data to parent (only the 1st child)..
When I do sleep(5) after the child1 sends data through pipe to parent then the 2nd child sends the same prime number to the parent.
Can someone help me?
//--------------------------Consts---------------------------------
#define NUM_OF_CHILDS 2
#define N 20
#define WIN 5
struct msg{
pid_t _pid;
int _prime;
};
//--------------------------Prototypes-----------------------------
bool is_prime(int num);
void terminate(pid_t child_pid[],int fd[2]);
void do_child(int fd[2]);
void print_pair(const int f_arr[],const int s_arr[]);
//--------------------------Main-------------------------------------
int main()
{
int f_arr[N] = {0},
s_arr[N] = {0},
ind, //running on children fork
count1 = 0,
count2 = 0,
victory1 = 0,
victory2 = 0,
min = 0;
int fd[2];
bool read1 = false,
read2 = false;
srand((unsigned)time(NULL));
pid_t child_pid [NUM_OF_CHILDS];//children pid status array
struct msg msg1;
if (pipe(fd) == -1)//pipe fd
{
perror("cannot open pipe");
exit(EXIT_FAILURE);
}
for(ind = 0; ind < NUM_OF_CHILDS; ind++)
{
child_pid[ind] = fork();// duplicate the current process
if (child_pid[ind] < 0)//fork failed
{
perror("Cannot fork()");
exit(EXIT_FAILURE);
}
if(child_pid[ind] == 0)/* child : sends message to parent*/
do_child(fd);
}
/* parent : receives message from child */
close(fd[1]); // close the write-end of the pipe
//read data from pipe
while(read(fd[0],&msg1,sizeof(struct msg)) > 0)
{
if(child_pid[0] == msg1._pid)
{
f_arr[count1++] = msg1._prime;
read1 = true;
}
else
{
s_arr[count2++] = msg1._prime;
read2 = true;
}
if(read1 && read2)
{
if(f_arr[min] > s_arr[min])
victory1++;
else if(f_arr[min] < s_arr[min])
victory2++;
read1 = false;
read2 = false;
min++;
}
if(victory1 == WIN || victory2 == WIN)
terminate(child_pid,fd);
}
close(fd[0]);// close the read-end of the pipe
print_pair(f_arr,s_arr);
return EXIT_SUCCESS ;
}
//---------------------------------------------------------------------
//checking if number is a prime number or not
//and return true or false
bool is_prime(int num)
{
int i;
if(num==0 || num==1 || num==2)
return false;
for(i=2;i<=num/2;i++)
{
//the number is not prime
if(num%i == 0)
return false;
}
//the number is prime
return true;
}
//----------------------------------------------------------------
void do_child(int fd[2])
{
struct msg message;
int num;
close(fd[0]);
while (1)
{
num = rand() % 1000;
if(is_prime(num))
{
message._prime = num;
message._pid = getpid();
write(fd[1], &message, sizeof(struct msg));
}
}
}
//----------------------------------------------------------------
void terminate(pid_t child_pid[],int fd[2])
{
int ind,
loop;
for(ind = 0; ind < NUM_OF_CHILDS; ind++)
{
close(fd[1]);
//first to give the process an opportunity to die gratefully before
//using SIGKILL
kill(child_pid[ind], SIGTERM);
bool died = false;
//It will give the process 5 seconds to die gracefully
for (loop = 0; loop < 5 && !died; ++loop)
{
int pid;
//the time the child process takes to close down gracefully.
sleep(1);
//to get the return status of that process and prevent zombie processes.
if (waitpid(child_pid[ind], &pid, WNOHANG) == child_pid[ind])
died = true;
}
//if SIGTERM did not killed the child do SIGKILL
if (!died)
{
int pid;
kill(child_pid[ind], SIGKILL);
waitpid(child_pid[ind], &pid, 0);// harvest the zombie
}
}
}
//------------------------------------------------------------------
void print_pair(const int f_arr[],const int s_arr[])
{
int ind;
for(ind = 0; ind < N; ind++)
{
if(f_arr[ind] == 0 && s_arr[ind] == 0)
break;
printf("(%d,%d)\n",f_arr[ind],s_arr[ind]);
}
}
First, the two child processes are generating the same pseudorandom sequence because they're starting with the same seed. To have any chance of different numbers, you need to seed them after the fork, and probably use something that changes more than once per second (the chance of the two of them having different values of time() is very small even if you moved the srand(time(NULL)) after the fork).
Second, you are receiving all the numbers from the first process because it has a head start. There's plenty of time to write to the pipe while the second process is being created. The parent doesn't start reading until after both children are created, so the first child fills the pipe buffer and then blocks. Pipe buffers are at least a few kilobytes.
Even when I slowed down the child process by making it print the numbers to stderr, the first one still generated hundreds of numbers before the second one got going.
So what happens in your main loop when there are hundreds of messages arriving from child 1 and none from child 2? Your f_arr array overflows because it only has room for 20. After that, anything can happen.
The simplest way to prevent that would be to check whether count1 == N before attempting to store a number into f_arr[count1++], and if so, just continue; to the next message. You should do the same for messages from the second child, even though it's not likely to happen.
This way you'll be accepting at most N messages from each child, and ignoring the rest. You'll need to add another end condition to the main loop: if both children have sent N messages, you need to stop.
Another way to go would be to use a separate pipe for each child and alternate reading from both pipes to keep them synchronized, but I have a feeling you were deliberately avoiding that.

Using multiple processes to read a file and sending numbers through pipe()

I have to use fork(2) to make as many children as inputted by the user.
Then I need them to split up the work reading a txt file of coordinate points comparing the distance between them to an inputted distance.
Then they add their count of how many points are within the distance given. Each child has to write their count to the pipe and the parent has to read each count and add it to the total and then print it out. Here is my code:
int main( int argc, char *argv[] ) {
int distance = atoi(argv[1]);
if ( argc != 3 || sscanf( argv[ 1 ], "%d", &distance ) != 1 )
fail( "usage: pairs <distance>" );
readPoints();
int workers = atoi(argv[2]);
// Compute the square of the distance bound, since that's what we'll
// need to compare against.
int dsq = distance * distance;
// Count up the number of nearby pairs of points.
int total = 0;
int fd[2]; // pipe
if ( pipe( fd ) != 0 ){
fail( "Can't create pipe" );
}
int pid; // child
int chNum; // child's number
int c;
for( chNum = 0; chNum < workers; chNum++){
c = 0;
pid = fork();
if ( pid == -1 ){ //failure
fail( "Can't create child process" );
}
if( pid ==0 ){ // it's a child
for ( int i =chNum; i < ptCount; i+=workers)
for ( int j = i + 1; j < ptCount; j++ ) {
// Check the squared distance.
int dx = ptList[ i ].x - ptList[ j ].x;
int dy = ptList[ i ].y - ptList[ j ].y;
if ( dx * dx + dy * dy <= dsq )
c++;
}
close(fd[READ]);
lockf(fd[WRITE], F_LOCK,0);
write(fd[WRITE], &c, sizeof(c));
lockf(fd[WRITE], F_ULOCK,0);
close(fd[WRITE]);
exit(0);
}
else if(pid>0){ // this is parent
int d;
close(fd[WRITE]);
read(fd[READ], &d, sizeof(d));
close(fd[READ]);
total = total + d;
}
}
if(pid>0){
wait(NULL);
printf( "Total: %d\n", total );
}
return 0;
}
I use a for loop to make the children with fork(2), and then I have them calculate the count and send it to the pipe to be read by the parent. The parent reads into d and adds it to total. I was wondering if I am using the pipe correctly to send each child's count to the parent and/or if I am forking correctly so it only comes from one parent. I am getting the wrong total count when I use more than 1 child.
If I use 1 child, the total result is 166428, which is correct, but when I use 4 for example, it gives me 164908. Can someone help me?
You're not doing pipe handling properly.
First off, you don't need to lock / unlock to write to and read from the pipe: writes that are less than PIPE_BUF bytes are guaranteed to be atomic. POSIX.1-2001 requires that PIPE_BUF is at least 512 bytes; since you're only writing sizeof(int) bytes at a time, you're safe (unless sizeof(int) is greater than or equal to 512, which is nonsense). See man limits.h, under Pathname Variable Values:
{PIPE_BUF}
Maximum number of bytes that is guaranteed to be atomic
when writing to a pipe. Minimum Acceptable Value: {_POSIX_PIPE_BUF}
That by itself simplifies the code and reduces unnecessary locking / unlocking overhead.
But the real issue is here:
else if (pid > 0) { // this is parent
int d;
close(fd[WRITE]);
read(fd[READ], &d, sizeof(d));
close(fd[READ]);
total = total + d;
}
You can't close fd[WRITE] inside the loop: consider what happens in the next iteration, when you fork the next process. The child process in the next loop will attempt to write to a file descriptor that has already been closed, so an error occurs (and write(2) fails with EBADF, but you never check the return value of write(2) so the code happily ignores the error). Plus, you attempt to close fd[WRITE] again and again, so close(2) will also return an error (which again, you ignore).
Similarly for read(2): if you close fd[READ], you can't read the results out of the pipe in the next iteration; read(2) will return an error and close(2) too.
(So the lesson is: do not ignore errors. If you had done error handling properly, you would have a pretty strong clue at what was going wrong)
You don't need to close. The child processes write exactly workers integers to the pipe; the parent process reads exactly workers integers from the pipe, so this is enough:
for (chNum = 0; chNum < workers; chNum++) {
c = 0;
pid = fork();
if (pid == -1)
fail("Can't create child process");
if (pid == 0) { // it's a child
for (int i = chNum; i < ptCount; i += workers) {
for (int j = i + 1; j < ptCount; j++) {
// Check the squared distance.
int dx = ptList[i].x - ptList[j].x;
int dy = ptList[i].y - ptList[j].y;
if (dx*dx + dy*dy <= dsq) {
c++;
}
}
}
ssize_t written = write(fd[WRITE], &c, sizeof(c));
if (written == -1)
perror("write error");
if (written != sizeof(c))
fail("Write failed on pipe");
exit(0);
}
else {
int d;
if (read(fd[READ], &d, sizeof(d)) != sizeof(d))
fail("Read error on pipe");
total += d;
}
}
The key point is to understand that you need to keep fd[READ] and fd[WRITE] open as long as you plan to fork new processes that will use the pipe.
Now, that fixes the problem, but you get a false sense of parallelism: reads in a pipe will block by default if no data is available. This means that on each iteration, the parent will not make progress until the corresponding child writes to the pipe. So you're not really parallelizing anything; the effect is the same as having the parent fork, wait for the child to terminate, read the result and add it to total, and then fork the next child (and repeat the cycle).
If you want true parallelism, you have to fork every process and only then start reading from the pipe. Something like this:
for (chNum = 0; chNum < workers; chNum++) {
c = 0;
pid = fork();
if (pid == -1)
fail("Can't create child process");
if (pid == 0) { // it's a child
for (int i = chNum; i < ptCount; i += workers) {
for (int j = i + 1; j < ptCount; j++) {
// Check the squared distance.
int dx = ptList[i].x - ptList[j].x;
int dy = ptList[i].y - ptList[j].y;
if (dx*dx + dy*dy <= dsq) {
c++;
}
}
}
ssize_t written = write(fd[WRITE], &c, sizeof(c));
if (written == -1)
perror("write error");
if (written != sizeof(c))
fail("Write failed on pipe");
exit(0);
}
}
if (close(fd[WRITE]) < 0)
fail("Error closing pipe's write channel");
int d;
ssize_t r;
while ((r = read(fd[READ], &d, sizeof(d))) > 0) {
if (r != sizeof(d))
fail("read error");
total += d;
}
Note that here we have to explicitly close the pipe's write channel before starting the reads; this is to avoid having the parent hang when no more child processes are actively writing to the pipe. Remember that a read will block as long as there is at least one process with the pipe's write channel open. If the parent process kept the write channel open, read(2) would never return because there is a chance that the parent himself could write to the pipe (even though we know it won't). So we have to close fd[WRITE].
Alternatively, since we know that there are exactly workers numbers to read from the pipe, we could just do this after the loop instead of closing the write channel:
int d;
int i;
for (i = 0; i < workers; i++) {
if (read(fd[READ], &d, sizeof(d)) != sizeof(d))
fail("Failed to read from pipe");
total += d;
}
A couple of other (unrelated) remarks:
The error message when the wrong arguments are given does not agree with the code. The code shows that distance is in argv[1] and workers is in argv[2], yet the error message passed to fail() seems to say that distance is in argv[2].
argv[1] is parsed twice as an integer: with atoi(3) and with sscanf(3). I'd stick to sscanf(3) since you can check the return value to make sure that parsing was successful.
workers is not validated and is converted with atoi(3). Errors are ignored. I'd suggest parsing it with sscanf(3) just like you did with distance and make sure that it is successful.
The correct type to store a pid is pid_t, not int. Please use the correct type (you might have to include sys/types.h in addition to unistd.h).
Here's the final version with all of this sorted out:
int main(int argc, char *argv[]) {
int distance;
int workers;
if (argc != 3 || sscanf(argv[1], "%d", &distance) != 1 || sscanf(argv[2], "%d", &workers) != 1)
fail("usage: <distance> <workers>");
readPoints();
// Compute the square of the distance bound, since that's what we'll
// need to compare against.
int dsq = distance * distance;
// Count up the number of nearby pairs of points.
int total = 0;
int fd[2]; // pipe
if (pipe(fd) != 0)
fail("Can't create pipe");
pid_t pid;
int chNum; // child's number
int c;
for (chNum = 0; chNum < workers; chNum++) {
c = 0;
pid = fork();
if (pid == -1)
fail("Can't create child process");
if (pid == 0) { // it's a child
for (int i = chNum; i < ptCount; i += workers) {
for (int j = i + 1; j < ptCount; j++) {
// Check the squared distance.
int dx = ptList[i].x - ptList[j].x;
int dy = ptList[i].y - ptList[j].y;
if (dx*dx + dy*dy <= dsq) {
c++;
}
}
}
ssize_t written = write(fd[WRITE], &c, sizeof(c));
if (written == -1)
perror("write error");
if (written != sizeof(c))
fail("Write failed on pipe");
exit(0);
}
}
if (close(fd[WRITE]) < 0)
fail("Error closing pipe's write channel");
int d;
ssize_t r;
while ((r = read(fd[READ], &d, sizeof(d))) > 0) {
if (r != sizeof(d))
fail("read error");
total += d;
}
printf("Total: %d\n", total);
return 0;
}

C - create two processes which can generate odd and even integers

I have this assignment where I have to create two processes and each process has to generate 50 integers which are odd or even.
Write a simple sequence-number system through which two processes, P1 and P2, can each obtain 50 unique integers, such that one receives all the odd and the other all the even numbers. Use the fork() call to create P1 and P2. Given a file, F, containing a single number, each process must perform the following steps:
a. Open F.
b. Read the sequence number N from the file.
c. Close F.
d. Output N and the process' PID (either on screen or test file).
e. Increment N by 1
f. Open F.
g. Write N to F.
h. Flush F.
i. Close F
As suggested by SO user I have created a loop in each process and ran the steps as mentioned above. But I am not sure if this approach is correct. I have asked my Teaching assistant for help and he suggested to do the same(using sleep call and waiting for a valid integer). But the thing is I can obtain the same results without using the sleep call. So I am not sure if I am applying the logic properly to code. Can someone please help?
This is my implementation:
void getUniqueNumbers() {
struct process p1;
struct process p2;
int numberFromFile;
pid_t pid = fork();
// Process 1
if (pid == 0) {
int p1Counter = 0;
p1.processId = getpid();
while(p1Counter < numLimit) {
numberFromFile = getNumberFromFile();
if (numberFromFile % 2 == 0) { // even
p1.numbers[p1Counter] = numberFromFile;
printf("N: %d, PID: %d\n", numberFromFile, p1.processId);
numberFromFile++;
writeNumberToFile(numberFromFile);
p1Counter++;
}
else {
sleep(1);
}
}
}
// Process 2
else if (pid > 0 ) {
int p2Counter = 0;
p2.processId = getpid();
while(p2Counter < numLimit) {
numberFromFile = getNumberFromFile();
if (numberFromFile % 2 != 0) { // odd
p2.numbers[p2Counter] = numberFromFile;
printf("N: %d, PID: %d\n", numberFromFile, p2.processId);
numberFromFile++;
writeNumberToFile(numberFromFile);
p2Counter++;
}
else {
sleep(1);
}
}
}
else {
printf("Error: Could not create process\n");
}
}
Read/Write functions:
// Returns the number included in user provided file
int getNumberFromFile() {
FILE *fp = fopen(fileName, "rb");
int num = 0;
if (fp != 0) {
char line[10];
if (fgets(line, sizeof(line), fp) != 0)
num = atoi(line);
fclose(fp);
}
return num;
}
// Writes a given number to the user provided file
void writeNumberToFile(int num) {
FILE *fp = fopen(fileName, "w");
if (fp != 0) {
fprintf(fp, "%d", num);
fclose(fp);
}
}
The code looks ok. It can be simplified a lot though.
void getUniqueNumbers()
{
struct process p; // We need only 1 structure
size_t counter = 0; // sample counter
int oddEven; // flag if we are parent
pid_t pid = fork(); // Fork here
if (-1 == pid)
{
abort(); // simply die on error
}
oddEven = 0 == pid ? 0 : 1;
p.processId = getpid(); // We are either child or parent.
while (counter < numLimit)
{
int numberFromFile = getNumberFromFile();
if ((numberFromFile & 1) == oddEven)
{
p.numbers[counter++] = numberFromFile;
printf("N: %d, PID: %ld\n", numberFromFile, (long)p.processId);
numberFromFile++;
writeNumberToFile(numberFromFile);
}
sleep(1); // sleep in both cases
// Extra check for parent: if child has died, we are in infinite
// loop, so check it here
if (0 != pid && counter < numLimit)
{
int status = 0;
if (waitpid(pid, &status, WNOHANG) > 0)
{
printf("Child exited with 0x%08X status\n", status);
break;
}
}
}
// wait till child process terminates
if (0 != pid)
{
int status = 0;
waitpid(pid, &status, 0);
printf("Child exited with 0x%08X status\n", status);
}
}
Also, the file reading/writing either should use file lock operations, or atomic file change. It is important to prevent potential errors like one thread is writing number 40006, and another one manages to read 400. Should not happen in real life though.
File locks are needed to prevent concurrent access to the same contents. It can be exclusive lock, or shared read exclusive write.
Atomic modifications are feature that enables to replace file contents atomically, regardless of how many operations it took to write the data. It is an alternative to keep data consistent.

Reading from pipe issue

I am writing on a pipe 10 integers, so i call write 10 times and then i want to call read pipe only once and store the written integers into an array of size 10 and after that add all the integers from the array into a total sum. The problem is that i get only 9 integers after reading. What i am doing wrong?
int main()
{
int fd[2];
int total = 0;
int result;
int nbytes;
int child;
int subVector;
int written;
static int readSum[P];
int partialSum;
if(pipe(fd) < 0){
perror("pipe");
}
for(child = 0; child < P; child++){
if((pid[child] = fork()) < 0){
perror("fork");
exit(1);
}
else if(pid[child] == 0){
close(fd[0]);
partialSum = getSubvectorSum(elementsList,child,P,SIZE);
//printf("Partial sum: %d by child #%d\n",partialSum,getpid());
written = write(fd[1],&partialSum,sizeof partialSum);
//printf("Child #%d has written: %d bytes.\n",getpid(),written);
if(written == 0){
printf("Writting not performed.");
}
close(fd[1]);
exit(0);
}
}
close(fd[1]);
int status = 0;
nbytes = read(fd[0],&readSum,sizeof readSum);
printf("Parent reads %d bytes\n",nbytes);
if(nbytes > 0){
for(child =0;child<P;child++){
total += readSum[child];
printf("Partial sum in father: %d\n",readSum[child]);
}
}
else{
printf("Failed to read.");
}
}
You are ignoring the wisdom of the sage Rolling Stones and not accepting that you can't always get what you want but sometimes you get what you need.
(1) There is no guarantee all your children have run and written to the pipe before the parent tries to read.
(2) There is no guarantee even if (1) did take place that your read would return all 10 integers in one read. read can (and often will) return less than you ask for.
One way to cover this is to have your parent wait on its children so you know they completed and then to read in a loop until you read everything you need.
http://linux.die.net/man/2/read
Read returns available data, not the requested amount, use cycle and check return value on each iteration.

Resources