I'm writing a very simple bash-like shell in C and am currently implementing pipes between commands (i.e. command1 | command2, which should run both commands at the same time with the stdout of the first one connected through a pipe with the stdin of the second one).
I've gotten to the point where something like
shell> echo test | cat | cat
correctly prints "test" to the string, but anything more complicated than that doesn't make it. For example:
shell> ls -1 / | sort | rev
It's (as far as I can tell) equivalent to the previous one in terms of piping, yet this one fails and the other one succeeds.
I'm at a complete loss as to why this is because I've debugged both the main process and the children exhaustively and verified that the processes get launched with the correct connections both in the working and in the not working command.
Here's a simplified version of the code:
// Uncomment to use hardcoded input
// #define USE_HARDCODED_INPUT
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // NULL
#include <errno.h> // ENOENT
#include <stdio.h> // setbuf, printf
#include <unistd.h> // exec, fork
#include <fcntl.h> // open
#include <sys/types.h> // wait
#include <sys/wait.h>
void set_process_FDs(int input, int output, int error)
{
if (input)
{
dup2(input, STDIN_FILENO);
close(input);
}
if (output)
{
dup2(output, STDOUT_FILENO);
close(output);
}
if (error)
{
dup2(error, STDERR_FILENO);
close(error);
}
}
void child_setup(char **argv, int input, int output, int error)
{
if (input || output || error)
set_process_FDs(input, output, error);
execvp(argv[0], argv);
perror("exec()");
exit(1);
}
int launch_process(char **argv, int is_last,
int input, int output, int error)
{
int status;
pid_t pid = fork();
switch(pid)
{
case -1:
perror("fork()");
return 0;
case 0:
child_setup(argv, input, output, error);
return 0;
default:
break;
}
if (is_last)
wait(&status);
return 1;
}
int run_commands(char ***argvv)
{
int no_commands_ran = 0;
int argc;
char **argv = argvv[0];
int in_pipe[2];
int out_pipe[2];
for (int i=0; (argv = argvv[i]); ++i)
{
pipe(out_pipe);
if (i == 0)
in_pipe[0] = 0;
if (!argvv[i+1])
{
close(out_pipe[0]);
close(out_pipe[1]);
out_pipe[1] = 0;
}
for (argc=0; argv[argc]; ++argc);
if (!launch_process(argv, !argvv[i+1],
in_pipe[0], out_pipe[1], 0))
break;
if (i != 0)
{
close(in_pipe[0]);
close(in_pipe[1]);
}
in_pipe[0] = out_pipe[0];
in_pipe[1] = out_pipe[1];
no_commands_ran = i + 1;
}
return no_commands_ran;
}
extern int obtain_order(); // Obtains an order from stdin
int main(void)
{
char ***argvv = NULL;
int argvc;
char *filev[3] = {NULL, NULL, NULL};
int bg;
int ret;
setbuf(stdout, NULL); // Unbuffered
setbuf(stdin, NULL);
while (1)
{
#ifndef USE_HARDCODED_INPUT
printf("%s", "shell> "); // Prompt
ret = obtain_order(&argvv, filev, &bg);
if (ret == 0) // EOF
{
fprintf(stderr, "EOF\n");
break;
}
if (ret == -1)
continue; // Syntax error
argvc = ret - 1; // Line
if (argvc == 0)
continue; // Empty line
if (!run_commands(argvv))
continue; // Error executing command
#else
argvc = 3;
char ***argvv1 = calloc(4, sizeof(char*));
argvv1[0] = calloc(3, sizeof(char*));
argvv1[0][0] = strdup("echo");
argvv1[0][1] = strdup("test");
argvv1[1] = calloc(2, sizeof(char*));
argvv1[1][0] = strdup("cat");
argvv1[2] = calloc(2, sizeof(char*));
argvv1[2][0] = strdup("cat");
char ***argvv2 = calloc(4, sizeof(char*));
argvv2[0] = calloc(4, sizeof(char*));
argvv2[0][0] = strdup("ls");
argvv2[0][1] = strdup("-1");
argvv2[0][2] = strdup("/");
argvv2[1] = calloc(4, sizeof(char*));
argvv2[1][0] = strdup("sort");
argvv2[2] = calloc(4, sizeof(char*));
argvv2[2][0] = strdup("rev");
printf("%s", "shell> echo test | cat | cat\n");
if (!run_commands(argvv1))
continue; // Error executing command
usleep(500);
printf("%s", "shell> ls -1 / | sort | rev\n");
if (!run_commands(argvv2))
continue; // Error executing command
printf("%s", "\nNo more hardcoded commands to run\n");
break;
#endif
}
return 0;
}
obtain_order() is a function located in the parser, which is a simple Yacc parser. It just fills the vector of argvs called argvv with whatever was input in the shell. In case anyone wants to try the code and see the problem, simply uncomment the #define at the beginning to see the behaviour you'd get from typing the problematic commands manually.
To start, your parent process does not wait for all of its child processes to complete their execution.
This call to wait does occur after the last child process has been spawned
if (is_last)
wait(&status);
but it does not necessarily wait for the last child process. That is to say, it will return when any one child process has completed execution (or an error occurs).
Properly waiting for all child processes to complete, at the end of run_commands,
/* ... */
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
return no_commands_ran;
exposes the fact that children after the first are hanging, as wait blocks execution of the parent program.
(After placing a few fprintf statements. █ here indicates program is blocking.)
shell> echo test | cat | cat
LOG: Child<30607> (echo)
LOG: Child<30608> (cat)
LOG: Child<30609> (cat)
LOG: Child<30607> process exited with status <0>
█
Without waiting for all child processes, you are creating orphan processes.
As for why these processes fail to terminate, this is due to the fact that certain file descriptors are not being closed.
The call to launch_process
launch_process(argv, !argvv[i+1], in_pipe[0], out_pipe[1], 0)
ensures that in_pipe[0] and out_pipe[1] are closed in the child process, but leaks any valid file descriptors in_pipe[1] or out_pipe[0]. With those leaked file descriptors still open in the child processes, the associated pipes remain valid, and thus the processes will continue to block while they wait for more data to arrive.
The quickest fix is to change launch_process to accept both pipes
int launch_process(char **argv, int is_last,
int input[2], int output[2], int error);
pass both pipes
if (!launch_process(argv, !argvv[i+1], in_pipe, out_pipe, 0))
close the excess file descriptors
case 0:
close(input[1]);
close(output[0]);
child_setup(argv, input[0], output[1], error);
return 0;
remove
if (is_last)
wait(&status);
and add the previously shown wait loop to the end of run_commands.
Here is a complete example of a working version of your program, with minimal refactoring.
Compile with -DDEBUG for some additional sleep time, in order to discover file descriptor leaks (there should not be any). Please read the extended comment in main.
#define _POSIX_C_SOURCE 200809L
#define USE_HARDCODED_INPUT
#define DEBUG_SLEEP_TIME 20
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
void set_process_FDs(int input, int output, int error)
{
if (input) {
dup2(input, STDIN_FILENO);
close(input);
}
if (output) {
dup2(output, STDOUT_FILENO);
close(output);
}
if (error) {
dup2(error, STDERR_FILENO);
close(error);
}
}
void child_setup(char **argv, int input, int output, int error)
{
if (input || output || error)
set_process_FDs(input, output, error);
#ifdef DEBUG
/* a sleep here should allow time to inspect
* `/proc/$PID/fd` for FD leaks, see `main` for details
* if the child process hangs you will have ample time, regardless
*/
sleep(DEBUG_SLEEP_TIME);
#endif
execvp(argv[0], argv);
perror("exec()");
exit(EXIT_FAILURE);
}
int launch_process(char **argv, int is_last,
int input[2], int output[2], int error)
{
pid_t pid = fork();
(void) is_last;
switch(pid) {
case -1:
perror("fork()");
return 0;
case 0:
fprintf(stderr, "LOG: Child<%ld> (%s)\n", (long) getpid(), *argv);
close(input[1]);
close(output[0]);
child_setup(argv, input[0], output[1], error);
return 0;
default:
break;
}
return 1;
}
int run_commands(char ***argvv)
{
int no_commands_ran = 0;
int in_pipe[2];
int out_pipe[2];
char **argv;
for (int i = 0; (argv = argvv[i]); ++i) {
pipe(out_pipe);
if (i == 0)
in_pipe[0] = 0;
if (!argvv[i+1]) {
close(out_pipe[0]);
close(out_pipe[1]);
out_pipe[1] = 0;
}
if (!launch_process(argv, !argvv[i+1], in_pipe, out_pipe, 0))
break;
if (i != 0) {
close(in_pipe[0]);
close(in_pipe[1]);
}
in_pipe[0] = out_pipe[0];
in_pipe[1] = out_pipe[1];
no_commands_ran = i + 1;
}
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
return no_commands_ran;
}
int main(void)
{
fprintf(stderr, "LOG: Parent ID: <%ld>\n", (long) getpid());
#ifdef USE_HARDCODED_INPUT
char ***argvv1 = calloc(4, sizeof(char*));
argvv1[0] = calloc(3, sizeof(char*));
argvv1[0][0] = "echo";
argvv1[0][1] = "test";
argvv1[1] = calloc(2, sizeof(char*));
argvv1[1][0] = "cat";
argvv1[2] = calloc(2, sizeof(char*));
argvv1[2][0] = "cat";
char ***argvv2 = calloc(4, sizeof(char*));
argvv2[0] = calloc(4, sizeof(char*));
argvv2[0][0] = "ls";
argvv2[0][1] = "-1";
argvv2[0][2] = "/";
argvv2[1] = calloc(2, sizeof(char*));
argvv2[1][0] = "sort";
argvv2[2] = calloc(2, sizeof(char*));
argvv2[2][0] = "rev";
puts("shell> echo test | cat | cat");
if (!run_commands(argvv1))
return EXIT_FAILURE;
/* usleep is deprecated */
nanosleep(&(struct timespec) { .tv_nsec = 5e5 }, NULL);
puts("shell> ls -1 / | sort | rev");
if (!run_commands(argvv2))
return EXIT_FAILURE;
puts("No more hardcoded commands to run");
#endif
#ifdef DEBUG
/* compile with -DDEBUG
* placing a sleep here to provide time to discover
* any file descriptor leaks
* inspect `ls -l /proc/$PID/fd`
* only the standard stream fds should exist (0, 1, 2) at
* either debug sleep
* see child_setup as well
*/
sleep(DEBUG_SLEEP_TIME);
#endif
}
Here is a cursory, annotated example of establishing a series of pipes and processes. It works similarly to your example, and might help to further showcase the order in which file descriptors must be opened, duplicated, and closed.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int valid(int fd)
{
return fd >= 0;
}
/* these safe_* functions are a non-operation when passed negative values */
void safe_close(int fd)
{
if (valid(fd) && !valid(close(fd)))
perror("close");
}
void safe_dup2(int old, int new)
{
if (valid(old) && valid(new) && !valid(dup2(old, new)))
perror("dup2");
}
void execute(char *args[][8], size_t length)
{
int channel[2] = { -1, -1 };
for (size_t i = 0; i < length; i++) {
/* get previous reader in parent */
int from = channel[0];
/* close previous writer in parent */
safe_close(channel[1]);
/* create current-writer-to-next-reader pipe */
if (!valid(pipe(channel)))
perror("pipe");
int to = (i < length - 1) ? channel[1] : -1;
if (0 == fork()) {
/* duplicate previous reader to stdin in child */
safe_dup2(from, fileno(stdin));
/* close previous reader in child */
safe_close(from);
/* close next reader in current child */
safe_close(channel[0]);
/* duplicate current writer to stdout in child */
safe_dup2(to, fileno(stdout));
/* close current writer in child */
safe_close(channel[1]);
execvp(args[i][0], args[i]);
perror("exec");
exit(EXIT_FAILURE);
}
/* close previous reader in parent */
safe_close(from);
}
/* close final pipe in parent */
safe_close(channel[0]);
safe_close(channel[1]);
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
}
int main(void)
{
char *argv[][8] = {
{ "echo", "test" },
{ "cat" },
{ "cat", "-n" }
};
execute(argv, 3);
char *argv2[][8] = {
{ "ls", "-1", "/" },
{ "sort" },
{ "rev" }
};
execute(argv2, 3);
}
Aside: As an edge case, 0 is a valid file descriptor. set_process_FDs is flawed in that if STDIN_FILENO is closed, and a new file descriptor is acquired, it may be zero. if (output) or if (error) may not behave as expected.
I am trying to run three execv("./test",execv_str) in parallel. And I need to print out success message when each of execv() completes successfully.
But now I get result as following:
username#username:~/Desktop/$./test -p
SUCCESS
SUCCESS
SUCCESS
username#username:~/Desktop/$ TESTING
TESTING
TESTING
The expected result will be:
username#username:~/Desktop/$./test -p
TESTING
SUCCESS
TESTING
SUCCESS
TESTING
SUCCESS
username#username:~/Desktop/$
Here is the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int fork_execv()
{
int status;
pid_t pid;
pid = fork();
/* Handling Child Process */
if(pid == 0){
char* execv_str[] = {"./test", NULL};
if (execv("./test",execv_str) < 0){
status = -1;
perror("ERROR\n");
}
}
/* Handling Child Process Failure */
else if(pid < 0){
status = -1;
perror("ERROR\n");
}
return status;
}
int main(int argc, char *argv[]){
if (argc == 1){
sleep(5);
printf("TESTING\n");
}
else{
int i;
for(i = 0; i < 3; ++i){
if (fork_execv() != -1){
printf("SUCCESS\n");
}
}
}
}
How to modify my code to make it work?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int fork_execv()
{
int status;
pid_t pid;
pid = fork();
/* Handeling Chile Process */
if(pid == 0){
char* execv_str[] = {"./test", NULL};
if (execv("./test",execv_str) < 0){
status = -1;
perror("ERROR\n");
}
}
/* Handeling Chile Process Failure */
else if(pid < 0){
status = -1;
perror("ERROR\n");
}
return pid;
}
void handler(int sig){
printf("SUCCESS\n");
}
int main(int argc, char *argv[]){
if (argc == 1){
sleep(5);
printf("TESTING\n");
}
else{
int i;
pid_t process_id;
for(i = 0; i < 3; ++i){
if ((process_id = fork_execv()) != -1){
if(process_id != 0){
signal(SIGCHLD, handler);
waitpid(process_id, NULL, 0);
}
}
}
}
}
Here what I would do. After the fork, I return the pid, check if it isn't 0 (so we are in the father process) and make the father wait for the son. To print "success", I bind the SIGCHLD signal that is triggered when a child process ends. Note that this is a little overkill and put print after the waitpid would have done the job. (But I like to bind signal.)
Hi I'm just working on signalling between two processes. I have a main process (eg. MAIN) which keeps on running. This MAIN is forked from a Wrapper process (eg. WRAP).
Here is my code which will actually launch the WRAP process, which in turn will create a child process as MAIN.
When certain initialization is completed in MAIN I would like to post a signal SIGUSR1, which will be captured by WRAP and does some other stuffs.
The problem with my code is when the signal is raised from MAIN it is never trapped by WRAP process. Pls. share your suggestions on this code or if there are any other ways to achieve this.
Thank you.
In MAIN process:
After Init is completed I have added this code,
main()
{
// Do some work here
int pid = GetProcessID(); // Returns the process ID of WRAP process
kill(pid,SIGUSR1); // Tries to send signal to WRAP process
// Other code
}
int GetProcessID()
{
int pid = 0;
char results[128];
FILE *fp = popen("pgrep WRAP", "r");
if(fp == NULL)
{
printf("Error: Failed to get Process ID");
}
else
{
while(fgets(results, 128, fp) != NULL)
{
pid = atoi(results);
}
pclose(fp);
}
return pid;
}
In WRAP process:
main()
{
int pid;
signal(SIGUSR1,InitComplete);
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
if (pid == 0)
{
/* child */
system("mainProc.out");
}
else
{
/* parent */
if(KeepListening() == 1)
printf("Init completed successfully\n");
}
return 0;
}
int KeepListening()
{
const int MAX_WAIT_TIME = 180;
int procStarted = 0;
int res = 0;
sigset_t origset;
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGWINCH);
sigaddset(&ss, SIGUSR1);
res = sigprocmask(SIG_BLOCK, &ss, &origset);
if(res)
{
printf("\nError: sigprocmask returned an error\n");
}
struct timespec theTimeout;
theTimeout.tv_nsec = 0;
theTimeout.tv_sec = MAX_WAIT_TIME;
int sig = 0;
siginfo_t theInfo;
memset(&theInfo, '\0', sizeof(theInfo));
int timedwaitcount = 0;
do
{
sig = sigtimedwait(&ss, &theInfo, &theTimeout);
if(sig < 0)
{
if(EAGAIN == errno)
{
timedwaitcount++;
}
else
{
PrintMessage("Error:Error occured with sigtimedwait\n");
}
}
else
{
timedwaitcount = 0;
}
if(SIGUSR1 == sig)
{
return 1;
}
}while(SIGWINCH == sig || 0 == sig);
return procStarted;
}
void InitComplete()
printf("InitComplete in MAIN. Signal Received.\n");
}
I prepared a short sample which demonstrates how it should work.
Source file test-exec.c for what you call WRAPPER:
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
static int sigUsr1Rcvd = 0;
enum { SleepTimeUS = 50000 /* us */ };
void onSigUsr1(int sig)
{
if (sig == SIGUSR1) sigUsr1Rcvd = 1;
}
int main(int argc, char **argv)
{
int pid; char buffer[20]; int status = 0;
/* report alive */
printf("%s started...\n", argv[0]);
/* install signal handler before fork() */
signal(SIGUSR1, &onSigUsr1);
/* fork child */
if (pid = fork()) { /* main process */
if (pid < 0) {
perror("ERROR in fork()");
return -1;
}
} else { /* child process */
if (execl("./test-exec-child", "test-exec-child", NULL)) {
perror("ERROR in execl()");
return -1;
}
return 0;
}
/* main process */
/* waiting for SIGUSR1 */
while (!sigUsr1Rcvd) usleep(SleepTimeUS);
printf("%s: Child inited.\n", argv[0]);
/* wait for termination of child */
wait(&status);
/* done */
printf("%s exiting...\n", argv[0]);
return 0;
}
Source code file test-exec-child.c for what you call MAIN:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
enum { SleepTimeS = 3 /* s */ };
int main(int argc, char **argv)
{
char buffer[20];
/* report alive */
printf("%s started...\n", argv[0]);
/* consume some time */
printf("%s: initializing...\n", argv[0]);
sleep(SleepTimeS);
printf("%s: done.\n", argv[0]);
/* send signal to parent */
kill(getppid(), SIGUSR1);
/* spend time until user feed-back */
printf("Press [ENTER] to continue...");
fgets(buffer, sizeof buffer, stdin);
/* done */
printf("%s exiting...\n", argv[0]);
return 0;
}
I compiled and tested this with gcc on cygwin:
$ gcc -o test-exec test-exec.c
$ gcc -o test-exec-child test-exec-child.c
$ ./test-exec
./test-exec started...
test-exec-child started...
test-exec-child: initializing...
...
test-exec-child: done.
./test-exec: Child inited.
Press [ENTER] to continue...
[ENTER]
test-exec-child exiting...
./test-exec exiting...
$
Attempting to learn C forking. It correctly prints the number of times the main loop is suppose to run and the right number of threads but the execution time is off and the program never terminates. Am I making an infinite amount of processes?
After some suggestions here is a cleaner version of the code. The old version is located below. The updated part is still creating to many child processes and never exiting. I'm just not seeing what's going wrong.
Update: Suggestion by John Hascall fixed a formatting and threads running out of order. An infinite number of threads are still generated but now in the correct order. I.e prints thread execution time 1, 2, 3, 4... etc. Don't think the problem is the wait syscall but going to study it and see if I can't find anything.
Update**: I found the solution. The first problem I believe was that I didn't have a wait command and the second is that when putting in the wait I accidentally removed the check for count < argv[1]. I put it back in and it seems to be running correctly! Thanks for the help and style pointers everyone! Working version is below.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "./processes.h"
int main(int argc, char** argv) {
if (argc != 4) {
printf("Wrong number of arguments entered. Usage: #processes sleepTime inputFile.\n");
return 1;
}
if(atoi(argv[1]) <= 0){
printf("Incorrect number of children, must be greater than 0");
return -1;
}
int count = 0;
int index;
Child *child = malloc(sizeof(Child) * atoi(argv[1]));
int childIndex;
int pid;
do{
switch (pid = fork()){
case -1:
printf("Fork failed\n");
exit(1);
case 0:
sleep(atoi(argv[2]) * childIndex);
gettimeofday(&child[childIndex].endTime, NULL);
double elapsed = child[childIndex].endTime.tv_usec - child[childIndex].startTime.tv_usec;
printf("Time for process %d = %f microseconds\n", childIndex, elapsed);
break;
default:
childIndex = count + 1;
gettimeofday(&child[count].startTime, NULL);
child[count].index = count + 1;
child[count].pid = pid;
count++;
}
} while((wait(NULL) != -1) && (count < atoi(argv[1])));
return 1;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "./processes.h"
int main(int argc, char** argv) {
if (argc != 4) {
printf("Wrong number of arguments entered. Try again.");
return 1;
}
if(atoi(argv[1]) <= 0){
printf("Incorrect number of children, must be greater than 0");
return -1;
}
int count;
int index;
Child *child = malloc(sizeof(Child) * atoi(argv[1]));
int pid = 1;
int childIndex;
for (count = 0; count < atoi(argv[1]); count++) {
if (pid != 0) {
childIndex = count + 1;
gettimeofday(&child[count].startTime, NULL);
child[count].index = count + 1;
pid = fork();
if (pid != 0){
child[count].pid = pid;
printf("Main thread loop: %d\n", count);
printf("Child process: %d\n", getpid());
}
}
}
if (pid == 0) {
//this is the child process
sleep(atoi(argv[2]) * childIndex);
gettimeofday(&child[childIndex].endTime, NULL);
double elapsed = child[childIndex].endTime.tv_usec - child[childIndex].startTime.tv_usec;
printf("Time for process %d = %f microseconds\n", childIndex, elapsed);
//printf("This is thread %d reporting in.\n", childIndex);
}
// printf("Testing\n");
return 1;
}
The bigest issue is your child's code:
if (pid == 0) {
....
}
belongs in the same loop (say right after) the parent's code:
if (pid != 0) {
....
}
Also, you never check for pid == -1 (the fork() failed).
A more standard way to write something like this is:
switch (pid = fork()) {
case -1:
/* handle fork error */
exit(1);
case 0:
/* child code goes here */
_exit(0);
default:
/* parent code goes here */
}
/* Also you probably want to look into the `wait()` syscall. */
do {} while (wait(NULL) != -1); /* <--- the very minimum */
I am writing a program that creates a pipe, forks, then the parent sends the command line arguments to the child one char at a time. The child is supposed to count them, and then the parent reaps the child and prints out how many arguments there were. Here is what I have so far:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc, char **argv)
{
pid_t pid;
int status;
int comm[2];
char buffer[BUFSIZ];
// set up pipe
if (pipe(comm)) {
printf("pipe error\n");
return -1;
}
// call fork()
pid = fork();
// fork failed
if (pid < 0) {
printf("fork error %d\n", pid);
return -1;
}
else 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, sizeof(buffer)) != '\n')
nChars++;
// Return number of characters counted to parent process.
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
close(comm[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
char endl='\n';
for (int a = 1; a < argc; a++) {
for (int c = 0; c < strlen(argv[a]); c++) {
write(comm[1], &argv[a][c], 1);
}
}
write(comm[1], &endl, 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.
waitpid(pid, &status, 0);
printf("child counted %d chars\n", nChars);
return 0;
}
}
It seems to run endlessly. It must be stuck in one of the loops. What is going wrong?
Code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc, char *argv[])
{
pid_t pid;
int status;
int comm[2];
char buffer[BUFSIZ];
// set up pipe
if (pipe(comm) < 0) {
printf("pipe error\n");
return -1;
}
// call fork()
if((pid = fork()) <0)
{
printf("fork error %d\n", pid);
return -1;
}
else if (pid == 0) {
// -- running in child process --
int nChars = 0;
close(comm[1]);
//printf("%d \n",BUFSIZ);
// Receive characters from parent process via pipe
// one at a time, and count them.
int n;
while( (n =read(comm[0], buffer, BUFSIZ)) >0)
{
buffer[n] = 0;
int oneChar, i = 0,endflag = 0;
while((oneChar = buffer[i])!=0)
{
// printf("%d\n",oneChar);
if(oneChar!=EOF)
nChars++;
else
{
endflag = 1;
break;
}
i++;
}
//printf("%s\n",buffer);
if(endflag)
break;
}
printf("nChar : %d",nChars);
// Return number of characters counted to parent process.
return nChars;
}
else {
// -- running in parent process --
//int nChars = 0;
close(comm[0]);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
int a,c;
char endl='\n';
for ( a = 1; a < argc; a++) {
for ( c = 0; c < strlen(argv[a]); c++) {
write(comm[1], &argv[a][c], 1);
}
}
printf("write end\n");
int end = EOF;
write(comm[1],&end,4);
waitpid(pid, &status, 0);
printf("child counted %d chars\n", WEXITSTATUS(status));
return 0;
}
}