Interprocess Communication in C, one character at a time - c

First off, this IS homework, I am not asking for an answer, however I am confused about something.
I have a homework assignment for a programming class, and I am a little confused about how to write the code the specific way that the instructor is asking.
The program first creates a child process, and then proceeds to send command line arguments from the parent process, through a pipe, ONE CHARACTER at a time to the child process, and then read them into the child process ONE CHARACTER at a time, incrementing the character count in the child process each time a character is read in.
I think I accomplished sending the data through the pipe one character at a time, but I have no idea how to "go" to the child process every time a character is sent, read it, increment the number of characters, and then go back to the parent process and repeat.
Here is my code, It works and gives accurate answers, but any tips on how to accomplish what my instructor is asking would be appreciated, thank you!!
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
// set up pipe
int pA[2];
char buff[50];
pipe(pA);
// call fork()
pid_t childId = fork();
if (childId == 0) {
// -- running in child process --
int nChars = 0;
// close the output side of pipe
close(pA[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
nChars = read(pA[0], buff, sizeof(buff)); //this line of code is what i need to change to be reading characters in one at a time
// Return number of characters counted to parent process.
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - Timothy Jensen\n");
// close the input side of the pipe
close(pA[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for (int i = 1; i < argc; i++)
{
size = strlen(argv[i]);
for (int z = 0; z < size; z++)
{
write(pA[1], &argv[i][z], 1);
}
}
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}

You need to make the following changes:
Make the last argument 1 in the call to read.
read(pA[0], buff, 1);
Put the above call in a while loop and increment nChar for every successful attempt at read.
while ( read(pA[0], buff, 1) == 1 )
{
++nChars;
}
Close the file descriptor from the parent process once you are done writing to it.
Here's a working version of main.
int main(int argc, char **argv)
{
// set up pipe
int pA[2];
char buff[50];
pipe(pA);
// call fork()
pid_t childId = fork();
if (childId == 0) {
// -- running in child process --
int nChars = 0;
// close the output side of pipe
close(pA[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
while ( read(pA[0], buff, 1) == 1 )
{
++nChars;
}
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - Timothy Jensen\n");
// close the input side of the pipe
close(pA[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for (int i = 1; i < argc; i++)
{
size = strlen(argv[i]);
for (int z = 0; z < size; z++)
{
write(pA[1], &argv[i][z], 1);
}
}
close(pA[1]);
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}

It seems a little silly, but you could change:
nChars = read(pA[0], buff, sizeof(buff));
to:
char ch;
nChars = read(pA[0], &ch, 1);
Of course, you would put the above into a loop to assemble a string 'one character at a time' back into buff.

Related

Put stdout in a pipe

I want to know the number of time a word is repeat in different file,
for that i need to use fork() function and create for each file a child that will find this number and add the different results in the parent.
I successfully find the number occurrences, but can't communicate this information to the parent.
I understand that I need to use pipe and dup2. I used them both previously, but separately, and I couldn't really say that I am comfortable with them.
As you can see with the variable 'temp', the pipe is empty. At first I thought that it was a synchronization problem but it doesn't seem to be the case. It is my understanding that dup2(tube[1],1) put stdout in the pipe, but I am starting to doubt.
What do I miss ?
int main(int argc, char const *argv[])
{
int tube[2];pipe(tube);int temp;
int s=0;
for (int i = 2; i < argc; i++)
{
if (fork()==0)
{
dup2(tube[1],1);
close(tube[1]);
close(tube[0]);
execlp("grep","grep","-c",argv[1],argv[i],NULL);
}
}
wait(NULL);
for (int i = 2; i < argc; i++) {
{
close(tube[1]);
close(tube[0]);
read(tube[0],&temp,sizeof(int));
printf("temp=%d\n",temp);
s+=temp;
}
}
printf("s=%d",s);
return 0;
}
After pipie(), the first file descriptor (FD) returned is the reader side of the pipe, the second FD is the writer side. So, you need to close the first FD in the child path, and the second DF in the parent path of your pogram.
You must not close the first FD in the parent path, since you want the parent to read what the client(s) wrote. And you must not close the second FD in the child path; how shall it be able to write to the pipe otherwise.
Remember that stdout is thought to be output for the user, i.e. text. So, your code needs to receive a character string (the count of matching lines), then, test for a valid number, and convert this to an int (long int, long long int), to be able to sum up.
Also, more than one child process may have written it's result before the parent reads from the pipe, i.e. multiple newline terminated strings may be read in a single read.
Finally, you need to wait() upon each child process, otherwise they will become zombies.
The problem made me curious, so I tried to come up with some working code.
//------------------------------------------------------------------------------
//
// The purpose of this program is to find the total number of lines containing
// the specified grep pattern in all the files specified.
//
// Parameters:
// pattern file [ file [ ... ] ]
//
// pattern -> search pattern to be passed to grep
// file -> file(s) to be scanned for "pattern" occurences.
//
//------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main( int argc, char* argv[] ) {
int pipefd[2];
int totalCount = 0;
//----------------------------------------
// Open a pipe to receive the result from
// running "grep" in the child processes.
//----------------------------------------
pipe(pipefd);
//---------------------------------------------------------
// Start a child process for each file given as parameter.
// First file is passed as argument #2.
//---------------------------------------------------------
for ( int ii = 2; ii < argc; ii++ ) {
//------------------------
// This is the child code
//------------------------
if ( fork() == 0 ) {
//-----------------------------
// Redirect stdout to the pipe
//-----------------------------
dup2( pipefd[1], 1 );
//------------------------------------
// Close the reader side of the pipe.
//------------------------------------
close( pipefd[0] );
execlp( "grep", "grep", "-c", argv[1], argv[ii], NULL );
}
}
//-----------------------------------------------------------------------
// This is the parent code.
//
// There possibly is more than one child process writing to the pipe.
// Writes and reads are atomic, however, more than one child may be able
// to write to the pipe before the parent can read.
//-----------------------------------------------------------------------
for ( int ii = 2; ii < argc; ii++ ) {
char result[1024];
int bytesRead;
int ss, pp;
//---------------------------
// Close writer side of pipe
//---------------------------
close( pipefd[1] );
//----------------------------------------------------------
// Read the data that one or more child process has written
//----------------------------------------------------------
bytesRead = read( pipefd[0], &result, sizeof( result ) );
if ( bytesRead > 0 ) {
//---------------------------------------------------------
// One or more *newline terminated* string has been read
// from the pipe, representing the result from one or
// more grep command that has finised.
//
// Each newline terminated string is converted to a zero
// terminated C-sytle string, so that it can be passed to
// atoi().
//---------------------------------------------------------
ss = 0;
for ( pp = 0; pp < bytesRead; pp++ ) {
if ( result[pp] == '\n' ) {
result[pp] = 0x0;
totalCount += atoi( &result[ss] );
ss = pp + 1;
}
}
}
wait( NULL );
}
//-----------------------------------
// Print the final result and return
//-----------------------------------
printf("Total number of matches: %d\n\n", totalCount );
return 0;
}
Edit from April 19th: Removed some leftover from intermediate code version. Clarified the conversion from newline terminated to zero terminated string.

Program hangs after receiving proper result from child processes through pipe()

I'm splitting a file, sending through pipe(), having children find the sum of their designated section of the file, returning the calculated sum to the parent through pipe(), and having the parent calculate the sum of the child sums.
I've got a working program. My issues is that it hangs after receiving showing the proper final value.
I'm not sure what I'm doing to have the parent expect more information, but I'd bet it has something to do with my for() loop containing my child code.
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int numchild;
int i, j, len, fpos=0, val, count=0, total=0, alltotal=0;
pid_t pid;
int nums = 1000;
FILE * file;
printf("How many children to use: ");
scanf("%d", &numchild);
printf("\nWill use %d child process(es).\n", numchild);
int fd[2*numchild][2]; //parent+child pipe
// create all pipes
for (i=0; i<2*numchild; i++)
{
pipe(fd[i]);
}
for (i=0; i<numchild; i++)
{
if((pid = fork()) == 0) // child process
{
pid = getpid();
// read from parent
len = read(fd[i][0], &fpos, sizeof(fpos));
if (len > 0)
{
file = fopen("file1.dat", "r");
fseek (file, fpos, SEEK_SET);
count = 0;
total = 0;
printf("Child(%d): Recieved position: %d\n", pid, fpos);
// read from file starting at fpos
// add values read to a total value
while (count < (nums/numchild))
{
fscanf(file, "%i", &val);
total += val;
count++;
}
//write to parent
write(fd[i+numchild][1], &total, sizeof(total));
printf("Child(%d): Sent %d to parent.\n", pid, total);
}
else
{
printf("Child(%d): Error with len\n", pid);
}
_exit;
}
// parent process
pid = getpid();
fpos = ((i*nums*5)/numchild); // 5 is the offset of the file values
// write to child process
printf("Parent(%d): Sending file position to child\n", pid);
write(fd[i][1], &fpos, sizeof(fpos));
// wait for child responce
len = read(fd[i+numchild][0], &total, sizeof(total));
if (len > 0)
{
printf("Parent(%d): Recieved %d from child.\n", pid, total);
alltotal += total;
printf("Parent(%d): Total: %d\n", pid, alltotal);
}
else
{
printf("Parent(%d): Error with len\n", pid);
}
}
}
I can't ask more questions, but if this is on Linux or Unix like systems (perhaps all posix):
You must do a wait (man 2 wait) for each of your child processes in your main program or you will create zombie processes.
Not knowing what environment you are running in makes it impossible for me to test this to determine if that is the cause of your not exiting properly.
Also (this is more like a comment), each cycle through the loop you are forking one child, then feeding it data, then getting a response, then printing the total. Is that really what you want to do? You don't need to create a bunch of pipes if you are only running one child at a time.
My guess is that you want to have some actual concurrency. You can do this by having a loop that creates all of the children followed by another loop that feeds them data, followed by a third loop that looks at the results and sums them, followed by a fourth loop that waits for each to terminate (to avoid zombies).
I would consider using poll or select to read the returns -- after all, there is no guarantee that the children will finish in order.
I won't select this as an answer until other people let me know this is probably what fixed it, but I believe that my program was hanging because the main() function wasn't actually returning anything. When I went to put in time collection (save begin time, end time, calculate difference, output) it had to be done outside of my for loop, so it was the last statement right inside my main() method. This seems to have stopped the hanging.
Right after calling fork() in your if-statement, you need to call wait(). This makes causes your parent to wait for your child to exit, and then continue he execution when the child exits.

Inter-process communication seems to be hanging

This is a program that was designed to take characters from the program call, pipe them one at a time to a child, count them in the child, return that value to the parent and print that value.
For some reason, the number of characters input isn't being displayed. It compiles without error and runs, but doesn't exit properly. This leads me to believe that the parent doesn't successfully reap the child and take the return value from it.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, char **argv)
{
int comm[2];
char buffer[50];
pid_t pid;
// set up pipe
pipe(comm);
// call fork()
pid = fork();
// code that runs in the child
if (pid == 0) {
// -- running in child process --
int nChars = 0;
close(comm[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
while(read(comm[0], buffer, 1) ==1) {
++nChars;
}
// Return number of characters counted to parent process.
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
int size = 0;
printf("CS201 - Assignment 3 - \n");
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
close(comm[0]);
for (int i = 1; i < argc ; i++) {
size = strlen(argv[i]);
for (int j = 0; j < size; j++) {
write(comm[1], &argv[i][j], 1);
}
}
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
wait(&nChars);
printf("child counted %d chars\n", nChars/256);
return 0;
}
}
Your parent process needs to close the pipe after it's done writing.
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
close(comm[0]);
for (int i = 1; i < argc ; i++) {
size = strlen(argv[i]);
for (int j = 0; j < size; j++) {
write(comm[1], &argv[i][j], 1);
}
}
close(comm[1]); // <--- add this

passing command line arguments to a child process and count them

I want the parent process to take the arguments to main() and send the characters in them one at a time to the child process through a pipe starting with argv[1] and continue through the rest of the arguments.(one call to write for each character).
I want the child process to count the characters sent to it by the parent process and print out the number of characters it received from the parent. The child process should not use the arguments to main() in any way whatsoever.
What am i doing wrong? do i need to use exec()?
output that isnt correct:
~ $ gc a03
gcc -Wall -g a03.c -o a03
~ $ ./a03 abcd ef ghi
child: counted 12 characters
~ $
here is the program..
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int length = 0;
int i, count;
int fdest[2]; // for pipe
pid_t pid; //process IDs
char buffer[BUFSIZ];
if (pipe(fdest) < 0) /* attempt to create pipe */
printf("pipe error");
if ((pid = fork()) < 0) /* attempt to create child / parent process */
{
printf("fork error");
}
/* parent process */
else if (pid > 0) {
close(fdest[0]);
for(i = 1; i < argc; i++) /* write to pipe */
{
write(fdest[1], argv[i], strlen(argv[1]));
}
wait(0);
} else {
/* child Process */
close(fdest[1]);
for(i = 0; i < argc; i++)
{
length +=( strlen(argv[i])); /* get length of arguments */
}
count = read(fdest[0], buffer, length);
printf("\nchild: counted %d characters\n", count);
}
exit(0);
}
You said that "the child process should not use the arguments to main() in any way whatsoever". However, I see that your child process is using argc. Doesn't this defeat your restriction?
You also say that you want "one call to write for each character". Your current implementation uses one call to write for each argument, not each character. Was this a typo? If not, you will want to use something more like this:
char nul='\0', endl='\n';
for (a=1; a < argc; ++a) {
for (c=0; c < strlen(argv[a]); ++c) {
write(fdest[1], &argv[a][c], 1);
}
write(fdest[1], &nul, 1);
}
write(fdest[1], &endl, 1);
This will write one character at a time, with each argument as a NULL-terminated string and a newline character at the end. The newline is only there to serve as a marker to indicate that there is no more data to send (and is safe to use since you won't be passing in a newline in a CLI argument).
The child process will just need to be a loop that reads incoming bytes one by one and increments a counter if the byte is not '\0' or '\n'. When it reads the newline character, it breaks out of the input processing loop and reports the value of the counter.
You have an error here:
write(fdest[1], argv[i], strlen(argv[1]));
You should take strlen(argv[i]) rather, or you're telling write() to read past the space of argv[i] and invoke undefined behavior.
Note that you're only calling read() once. By the time you're calling read(), perhaps only one of the argv[]s have been written by the parent. Or 2. Or any number of them.
The problem is here
write(fdest[1], argv[i], strlen(argv[1]));
Notice that this is strlen of argv[1], it should be argv[i]. You are actually referencing past the end of argv[2] and argv[3] in this loop
You are effectively writing strlen("abcd") * 3 characters which is 12 chars
In here:
for(i = 1; i < argc; i++) /* write to pipe */
{
write(fdest[1], argv[i], strlen(argv[1]));
}
strlen(argv[1]) should be in fact strlen(argv[i])

parent process, and a child process

I am trying to write a program that The parent process will take the arguments to main() and send the characters in them one at a time to the child process through a pipe (one call to write for each character). The child process will count the characters sent to it by the parent process and print out the number of characters it received from the parent. The child process should not use the arguments to main() in any way whatsoever. The child should return normally and not have the parent kill the child.
Am i counting the arguments right? am i sending the arguments in one at a time, and am i reaping the child?
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define size = 100;
int main(int argc, char *argv[])
{
int i, count =0;
int c;
int fdest[2]; // for pipe
pid_t pid; //process IDs
char buffer[BUFSIZ];
if (pipe(fdest) < 0) /* attempt to create pipe */
perror( "pipe" );
if ((pid = fork()) < 0) /* attempt to create child / parent process */
{
perror( "fork" );
}
/* parent process */
else if (pid > 0) {
close(fdest[0]);
for (i=1; i < argc; ++i)
{
for (c=0; c < strlen(argv[i]); ++c) {
write(fdest[1], &argv[i][c], 1);
}
}
close(fdest[1]);
wait(NULL);
exit(0);
} else {
/* child Process */
close(fdest[1]);
while (read(fdest[0], &buffer, 1) > 0)
{
count++;
}
printf("\nchild: counted %d characters\n", count);
}
wait(NULL);
exit(0);
}
The second wait() is superfluous; the child has no children of its own to wait for. The second 'exit(0);' could be replaced by 'return(0);'. You could omit the previous 'exit(0);' too.
The '#define size = 100;' is unused, which is just as well since the '=' makes it unusable for most purposes (and the semi-colon is a bad idea too - seldom does a macro end with a semi-colon). It should be '#define size 100' or 'enum { size = 100 };'. Often, people use upper case names for 'manifest constants', hence 'enum { SIZE = 100 };.
If you are reading one character at a time, you really don't need a buffer of size BUFSIZ (which is usually 512 or larger).
Also, it is a bad idea to do 'for (c = 0; c < strlen(argv[c]); c++)' because that calculates the length of the string on each iteration. Replace it with either of these:
for (const char *str = argv[i]; *str != '\0'; str++)
write(fdest, str, 1);
for (c = 0, len = strlen(argv[i]); c < len; c++)
write(fdest[1], &argv[i][c], 1);
You close the unused ends of the pipes - that is a crucial step to making things work correctly.
The code seems to be counting correctly. It works off the shelf when I test it. Why are you suspicious that it does not work?

Resources