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?
Related
How can I display the number of processes created?
(without using a formula)
for (i=0; i<3; i++)
fork();
count = count + 1;
printf("%d",count);
There are a number of ways to do this, and a good technique is to have each child write one byte into a file descriptor which the original process can read. Note that, for the sake of brevity, the following code contains absolutely no error checking. Also, we report only the number of spawned processes (7) rather than counting the original to get a count of 8:
int main(void) {
int fd[2];
int depth = 0; /* keep track of number of generations from original */
int i;
pipe(fd); /* create a pipe which will be inherited by all children */
for(i=0; i<3; i++) {
if(fork() == 0) { /* fork returns 0 in the child */
write(fd[1], &i, 1); /* write one byte into the pipe */
depth += 1;
}
}
close(fd[1]); /* exercise for the reader to learn why this is needed */
if( depth == 0 ) { /* original process */
i=0;
while(read(fd[0],&depth,1) != 0)
i += 1;
printf( "%d total processes spawned", i);
}
return 0;
}
Printing the count value out just once is the easy part. Because you can get the process pid before the for loop. And then get the pid again after the for loop and only print if the pids match. For the counting part, it depends on whether your child processes exit or not. If they exit the solution is easier. The below code demonstrates one possible solution if the child processes exit (for brevity have not done full error checking). The idea is that each child process counts its own children. Parent waits for each child to complete and adds in its count. Haven't had time to fully test/debug the program so there may be some errors. But hopefully gives you the general idea.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
pid_t before_pid, after_pid;
pid_t forked_pid;
int count;
int i;
int status;
before_pid = getpid();
count = 1; /* count self */
for (i = 0; i < 3; i++) {
forked_pid = fork();
if (forked_pid > 0) {
waitpid(forked_pid, &status, 0);
/* parent process - count child and descendents */
count += WEXITSTATUS(status);
} else {
/* Child process - init with self count */
count = 1;
}
}
after_pid = getpid();
if (after_pid == before_pid) {
printf("%d processes created\n", count);
}
return (count);
}
How can I display the number of processes created?
(without using a formula)
for (i=0; i<3; i++)
fork();
count = count + 1;
printf("%d",count);
There are a number of ways to do this, and a good technique is to have each child write one byte into a file descriptor which the original process can read. Note that, for the sake of brevity, the following code contains absolutely no error checking. Also, we report only the number of spawned processes (7) rather than counting the original to get a count of 8:
int main(void) {
int fd[2];
int depth = 0; /* keep track of number of generations from original */
int i;
pipe(fd); /* create a pipe which will be inherited by all children */
for(i=0; i<3; i++) {
if(fork() == 0) { /* fork returns 0 in the child */
write(fd[1], &i, 1); /* write one byte into the pipe */
depth += 1;
}
}
close(fd[1]); /* exercise for the reader to learn why this is needed */
if( depth == 0 ) { /* original process */
i=0;
while(read(fd[0],&depth,1) != 0)
i += 1;
printf( "%d total processes spawned", i);
}
return 0;
}
Printing the count value out just once is the easy part. Because you can get the process pid before the for loop. And then get the pid again after the for loop and only print if the pids match. For the counting part, it depends on whether your child processes exit or not. If they exit the solution is easier. The below code demonstrates one possible solution if the child processes exit (for brevity have not done full error checking). The idea is that each child process counts its own children. Parent waits for each child to complete and adds in its count. Haven't had time to fully test/debug the program so there may be some errors. But hopefully gives you the general idea.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
pid_t before_pid, after_pid;
pid_t forked_pid;
int count;
int i;
int status;
before_pid = getpid();
count = 1; /* count self */
for (i = 0; i < 3; i++) {
forked_pid = fork();
if (forked_pid > 0) {
waitpid(forked_pid, &status, 0);
/* parent process - count child and descendents */
count += WEXITSTATUS(status);
} else {
/* Child process - init with self count */
count = 1;
}
}
after_pid = getpid();
if (after_pid == before_pid) {
printf("%d processes created\n", count);
}
return (count);
}
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
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int i;
int fd[2];
int values[argc - 1];
for( i = 1; i < argc; i++ ) {
pipe(fd);
switch( fork()) {
case 0: /* child */
/* Do stuff */
close(fd[0]);
int value = atoi(argv[i]);
write(fd[1], &value, sizeof(value));
exit( 0 );
case -1:
perror( "fork" );
exit(1);
default: /* parent */
close(fd[1]);
read(fd[0], &values[i - 1], sizeof(values[i - 1]));
/* do stuff, but don't wait() or terminate */
}
}
for (i = 0; i < (argc - 1); i++)
{
printf("%d\n", values[i]);
}
return 0;
}
im trying to create as many processes as the number of arguments given to the executable and have each process pipe the argument to the parent and store into an array and at the end print out the elements of the array. since the processes run in parallel, when i print out the array elements, the order should be random with respect to the order i entered these elements, but that doesnt seem to be the case as i have ran the executable 1 million times, can someone tell me what the problem is? So someone kindly pointed out that read serializes things, what should i do to make the processes truly parallel?
Your pipe read in the parent serializes things.
You (1) create the child; (2) child writes to the pipe; (3) parent will block on read until there is something to read. This repeats for all the children.
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])