Broken phone app in c with pipes and fork [duplicate] - c

So I have a project to do but I am completely stumped. I have spent ten hours and have gotten nowhere. I don't specifically want the code to the answer, but some pseudocode and good hints in the right direction would help a heap!!
It forks a number of processes, k - a command-line argument, connected by pipes - each process is connected to the next, and the last process is connected to the first. Process number k sends its message on to process number (k+1)%n.
Process 0 reads a line from stdin. It then transmits it to process 1. Each other process reads the line, increments the first byte of the string by 1, and then relays the line to the next process. As it relays, it prints a status message (shown below).
When the message gets back to process 0, it is output to the standard output as well. When a process receives EOF (either from pipe, if its a process other than 0, or from stdin, for process 0), it prints the final string. This will close all pipes.
The expected output is:
$ ./ring 4
hello
process #0 (32768) sending message: hello
process #1 (32769) relaying message: iello
process #2 (32770) relaying message: jello
process #3 (32767) relaying message: kello
I hear kello
^C
$
What I have written so far:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFER_SIZE 80
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[])
{
char readmsg[BUFFER_SIZE], readmsg2[BUFFER_SIZE], final[BUFFER_SIZE];
int pid, process;
int parent_child[2], child_parent[2];
process = 0;
if (pipe(child_parent) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
if (pipe(parent_child) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid > 0) {
/* PARENT */
read(0, &readmsg, BUFFER_SIZE);
printf("process #%d (%d) sending message: %s",
0, getpid(), readmsg);
write(parent_child[WRITE_END], &readmsg, BUFFER_SIZE);
close(parent_child[WRITE_END]);
} else {
/* CHILD */
read(parent_child[READ_END], &readmsg2, BUFFER_SIZE);
readmsg2[0] += 1;
printf("process #%d (%d) relaying message: %s",
1, getpid(), readmsg2);
process += 1;
write(child_parent[WRITE_END], &readmsg2, BUFFER_SIZE);
}
read(child_parent[READ_END], &final, BUFFER_SIZE);
printf("I hear %d %s", pid - getpid(), final);
return 0;
}
What it does currently is read in a string from stdin, pass it to the first process and print process 0 (can't actually get the 0 though, simply printing 0), it then pipes the string to process 1 which distorts byte 1 and then writes to a pipe again and then outside of the pipes, the string is read and outputs the distorted string.
$ ./ring
hello
process #0 (6677) sending message: hello
process #1 (6678) relaying message: iello
I hear -6678 iello
^C
$
I don't know where to go from here. Thank you in advance, anything will help!!
Given some help this is what I have now:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFER_SIZE 80
#define READ_END 0
#define WRITE_END 1
int main(int argc, char **argv) {
char buf[BUFFER_SIZE];
int process, rings, pid, pid_n, pid_n1, pid_1, i;
int Pn[2]; //Pipe for process n -> 0
int Pn_1[2]; //Pipe for process n-1 -> n
int Pn_2[2]; //Pipe for process n-2 -> n-1
int P_0[2]; //Pipe for process 0 -> 1
process = 0;
if (argc == 2) {
rings = atoi(argv[1]);
} else {
fprintf(stderr, "Usage: %s n, where n is number of rings\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid == 0) {
if ((pid_n = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid_n == 0) {
/* CHILD */
close(Pn[WRITE_END]);
close(Pn_1[READ_END]);
} else {
/* PARENT */
close(Pn[READ_END]);
close(Pn_1[WRITE_END]);
}
for (i = 0; i < rings; i++) {
if ((pid_n1 = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid_n1 == 0) {
/* CHILD */
close(Pn_1[WRITE_END]);
close(Pn_2[READ_END]);
} else {
/* PARENT */
close(Pn_1[READ_END]);
close(Pn_2[WRITE_END]);
}
}
/* Not sure about these last ones */
if ((pid_1 = fork()) < 0) {
fprintf(stderr, "Fork failed");
return 1;
} else if (pid_1 == 0) {
/* CHILD */
close(P_n2[WRITE_END]);
close(P_0[READ_END]);
} else {
/* PARENT */
close(P_n2[READ_END]);
close(P_0[WRITE_END]);
}
} else {
/* PARENT */
read(0, &buf, BUFFER_SIZE);
buf[BUFFER_SIZE - 1] = '\0';
printf("process first # (%d) sending message: %s", getpid(), buf);
write(P_0[WRITE_END], &buf, BUFFER_SIZE);
read(Pn[READ_END], &buf, BUFFER_SIZE);
buf[BUFFER_SIZE - 1] = '\0';
printf("I hear %s", buf);
}
return 0;
}

This is a diagram I drew for myself showing how the processes are to be interconnected:
p4
C5 <--------- C4
/ \
p5 / p3 \
/ \
o----> C0 ---->o C3
\ /
p0 \ p2 /
\ /
C1 ---------> C2
p1
The Cn represent the processes; C0 is the parent process. The pn represent the pipes; the other two lines are standard input and standard output. Each child has a simple task, as befits children. The parent has a more complex task, mainly ensuring that exactly the right number of file descriptors are closed. In fact, the close() is so important that I created a debugging function, fd_close(), to conditionally report on file descriptors being closed. I used that too when I had silly mistakes in the code.
The err_*() functions are simplified versions of code I use in most of my programs. They make error reporting less onerous by converting most error reports into a one-line statement, rather than requiring multiple lines. (These functions are normally in 'stderr.c' and 'stderr.h', but those files are 750 lines of code and comment and are more comprehensive. The production code has an option to support prefixing each message with a PID, which is also important with multi-process systems like this.)
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
enum { BUFFER_SIZE = 1024 };
typedef int Pipe[2];
static int debug = 0;
static void fd_close(int fd);
/* These functions normally declared in stderr.h */
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_usage(char const *usestr);
static void err_remark(char const *fmt, ...);
static void be_childish(Pipe in, Pipe out)
{
/* Close irrelevant ends of relevant pipes */
fd_close(in[1]);
fd_close(out[0]);
char buffer[BUFFER_SIZE];
ssize_t nbytes;
while ((nbytes = read(in[0], buffer, sizeof(buffer))) > 0)
{
buffer[0]++;
if (write(out[1], buffer, nbytes) != nbytes)
err_sysexit("%d: failed to write to pipe", (int)getpid());
}
fd_close(in[0]);
fd_close(out[1]);
exit(0);
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
int nkids;
if (argc != 2 || (nkids = atoi(argv[1])) <= 1 || nkids >= 10)
err_usage("n # for n in 2..9");
err_remark("Parent has PID %d\n", (int)getpid());
Pipe pipelist[nkids];
if (pipe(pipelist[0]) != 0)
err_sysexit("Failed to create pipe #%d", 0);
if (debug)
err_remark("p[0][0] = %d; p[0][1] = %d\n", pipelist[0][0], pipelist[0][1]);
for (int i = 1; i < nkids; i++)
{
pid_t pid;
if (pipe(pipelist[i]) != 0)
err_sysexit("Failed to create pipe #%d", i);
if (debug)
err_remark("p[%d][0] = %d; p[%d][1] = %d\n", i, pipelist[i][0], i, pipelist[i][1]);
if ((pid = fork()) < 0)
err_sysexit("Failed to create child #%d", i);
if (pid == 0)
{
/* Close irrelevant pipes */
for (int j = 0; j < i-1; j++)
{
fd_close(pipelist[j][0]);
fd_close(pipelist[j][1]);
}
be_childish(pipelist[i-1], pipelist[i]);
/* NOTREACHED */
}
err_remark("Child %d has PID %d\n", i, (int)pid);
}
/* Close irrelevant pipes */
for (int j = 1; j < nkids-1; j++)
{
fd_close(pipelist[j][0]);
fd_close(pipelist[j][1]);
}
/* Close irrelevant ends of relevant pipes */
fd_close(pipelist[0][0]);
fd_close(pipelist[nkids-1][1]);
int w_fd = pipelist[0][1];
int r_fd = pipelist[nkids-1][0];
/* Main loop */
char buffer[BUFFER_SIZE];
while (printf("Input: ") > 0 && fgets(buffer, sizeof(buffer), stdin) != 0)
{
int len = strlen(buffer);
if (write(w_fd, buffer, len) != len)
err_sysexit("Failed to write to children");
if (read(r_fd, buffer, len) != len)
err_sysexit("Failed to read from children");
printf("Output: %.*s", len, buffer);
}
fd_close(w_fd);
fd_close(r_fd);
putchar('\n');
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
err_remark("%d exited with status 0x%.4X\n", corpse, status);
return 0;
}
static void fd_close(int fd)
{
if (debug)
err_remark("%d: close(%d)\n", (int)getpid(), fd);
if (close(fd) != 0)
err_sysexit("%d: Failed to close %d\n", (int)getpid(), fd);
}
/* Normally in stderr.c */
static const char *arg0 = "<undefined>";
static void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
static void err_usage(char const *usestr)
{
fprintf(stderr, "Usage: %s %s\n", arg0, usestr);
exit(1);
}
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
exit(1);
}
static void err_remark(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
Example output:
$ ./pipecircle 9
Parent has PID 34473
Child 1 has PID 34474
Child 2 has PID 34475
Child 3 has PID 34476
Child 4 has PID 34477
Child 5 has PID 34478
Child 6 has PID 34479
Child 7 has PID 34480
Child 8 has PID 34481
Input: Hello
Output: Pello
Input: Bye
Output: Jye
Input: ^D
34474 exited with status 0x0000
34477 exited with status 0x0000
34479 exited with status 0x0000
34476 exited with status 0x0000
34475 exited with status 0x0000
34478 exited with status 0x0000
34480 exited with status 0x0000
34481 exited with status 0x0000
$

It seems to me that you are pretty close, as this works for two processes. What you need to do now is loop to create more processes from the parent.
(k=N+1 processes: proc0 = parent, proc1, ..., procN)
Create a pipe Pn, that will be for procN->proc0
Create a pipe Pn-1, that will be for procN-1->procN
Create relaying fork procN
fork closes Pn output and Pn-1 input
parent closes Pn input and Pn-1 output
(loop here)
Create a pipe Pi-2, that will be for procI-2->procI-1
Create relaying fork procI-1
fork closes Pi-1 output and Pi-2 input
parent closes Pi-1 input and Pi-2 output
...
Create a pipe P0 that will be for proc0->proc1
Create relaying fork proc1
fork closes P1 output and P0 input
parent closes P1 input and P0 output
(end loop)
(parent final code:)
Read from stdin
Write on P0
Read on Pn
Write on stdout
Once created with fork(), the child processes (i.e. apart from proc0) close the input of the pipe (the output of the other is already closed!), read the message on one, write on the other and exit.
Some remarks on your current code:
The child shouldn't execute that list bit, when you read on child_parent.
You don't need that many buffers (you only need one, that will turn into one per process after the fork).
Put some terminating null bytes before printing :)
It's good practice to close the ends that you're not going to need

Related

Pipe 2 string using read/write after fork

I´m having troubles to understand how pipes and fork work together for a online course. Firstly, I want to type two strings below a printf and the when all read/write end cat both string into one.
I´m stuck in the read/write for both childs, the problems is that it didn´t match what I want because first print the strings and then open the channel to type. Maybe this is a small matter but I am starting using C for a online course. read and write function are compulsory.
String 1:
sdfsdfsdf
String 2:
Sdfsdfdsfsdfsd
String cat:
sdfsdfsdfSdfsdfdsfsdfsd
here is my code so far.
int main()
{
int fd1[2],fd2[2];
int status, pid;
pipe(fd1);
printf("String 1: \n");
pid = fork();
if(pid == 0) /* child 1*/
{
close(fd1[0]);
char cad1[100];
read(0,&cad1,100);
write(fd1[1],&cad1, 100);
close(fd1[1]);
exit(0);
}
else /* father*/
{
close(fd1[1]);
printf("String 2: \n");
pipe(fd2);
pid = fork();
if(pid == 0) /* child 2 */
{
close(fd2[0]);
close(fd1[0]);
char cad2[100];
read(0,&cad2,100);
write(fd2[1],&cad2, 100);
close(fd2[1]);
exit(0);
}
}
close(fd2[0]);
/* wait every child */
wait(&status);
wait(&status);
return 0;
}
My output is like this:
String 1:
String 2:
cvbcvbcvbcvb
cvbcvbcvbcvb
To cat my code (not implemented in the code I think to call both pipe into to char [] variable before cat.
Any suggestion to improve my code.
Thanks in advance.
Here's my code, based loosely on yours. As I said in a comment, the parent code needs to read from the two pipes to collect the data from the children, and then concatenate them. You need to pay attention to how many bytes are read so that you don't write more bytes than were read. The second child should close both fd1[0] and fd1[1] as it will use neither of them. You'll need to worry about newlines and null bytes — the information returned by read() is not a string and will include the newline.
I opted to use fgets() to read from standard input since it does return a string and zapped the newlines. I wrote some information to standard output, too. The concatenated string is created using snprintf().
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void syserr_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(EXIT_FAILURE);
}
int main(void)
{
int fd1[2], fd2[2];
int pid1, pid2;
if (pipe(fd1) < 0 || pipe(fd2) < 0)
syserr_exit("Failed to create two pipes: ");
else if ((pid1 = fork()) < 0)
syserr_exit("Failed to fork() child 1: ");
else if (pid1 == 0)
{
/* Child 1*/
close(fd1[0]);
close(fd2[0]);
close(fd2[1]);
char cad1[100];
printf("String 1: \n");
fflush(stdout);
if (fgets(cad1, sizeof(cad1), stdin) == NULL)
syserr_exit("child 1 - failed to read from standard input: ");
cad1[strcspn(cad1, "\r\n")] = '\0';
write(fd1[1], cad1, strlen(cad1));
/* Should error check the write! */
printf("child 1 wrote [%s] to the parent process\n", cad1);
close(fd1[1]);
exit(0);
}
else if ((pid2 = fork()) < 0)
syserr_exit("Failed to fork child 2: ");
else if (pid2 == 0)
{
/* Child 1*/
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
printf("String 2: \n");
fflush(stdout);
char cad2[100];
if (fgets(cad2, sizeof(cad2), stdin) == NULL)
syserr_exit("child 2 - failed to read from standard input: ");
cad2[strcspn(cad2, "\r\n")] = '\0';
write(fd2[1], cad2, strlen(cad2));
/* Should error check the write! */
printf("child 2 wrote [%s] to the parent process\n", cad2);
close(fd2[1]);
exit(0);
}
else
{
/* Parent */
char buffer1[100];
char buffer2[100];
close(fd2[1]);
close(fd1[1]);
ssize_t sz1 = read(fd1[0], buffer1, sizeof(buffer1));
buffer1[sz1] = '\0';
close(fd1[0]);
ssize_t sz2 = read(fd2[0], buffer2, sizeof(buffer2));
buffer2[sz2] = '\0';
close(fd2[0]);
size_t tlen = sz1 + sz2 + sizeof("[]+[]");
char concat[tlen];
snprintf(concat, sizeof(concat), "[%s]+[%s]", buffer1, buffer2);
/* wait for both children */
int s1, s2;
int c1 = wait(&s1);
int c2 = wait(&s2);
printf("The one child (%d) exited with status 0x%.4X\n", c1, s1);
printf("T'other child (%d) exited with status 0x%.4X\n", c2, s2);
printf("Received from %d (%zu bytes) [[%s]]\n", pid1, sz1, buffer1);
printf("Received from %d (%zu bytes) [[%s]]\n", pid2, sz2, buffer2);
printf("Concatenated data: <<%s>>\n", concat);
}
return 0;
}
One sample run yielded:
$ ./pipe43
String 1:
String 2:
Vultures keep the world clean.
child 2 wrote [Vultures keep the world clean.] to the parent process
Hypersonic transport planes are as ubiquitous as pink elephants
child 1 wrote [Hypersonic transport planes are as ubiquitous as pink elephants] to the parent process
The one child (69005) exited with status 0x0000
T'other child (69004) exited with status 0x0000
Received from 69004 (63 bytes) [[Hypersonic transport planes are as ubiquitous as pink elephants]]
Received from 69005 (30 bytes) [[Vultures keep the world clean.]]
Concatenated data: <<[Hypersonic transport planes are as ubiquitous as pink elephants]+[Vultures keep the world clean.]>>
$

Pipe between children

I want to do a program that first creates 3 processes (A) and later, creates one process more (B) and these first processes must write in a pipe that the last process read each time that process write.
I tried something but I don't know the way to do that because the process (B) is created after the processes (A)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CHILDREN 3
int main(int argc, char *argv[])
{
pid_t pid;
int fd[2];
char buffer[100];
char str[] = "Hello";
char str2[] = "Hello2";
char str3[] = "Hello3";
for(int num_process = 0; num_process < MAX_CHILDREN; num_process++)
{
if(pipe(fd) == -1)
{
perror( "pipe Failed" );
continue;
}
pid = fork();
if(pid < 0)
{
perror("fork failed");
exit(1);
}
if(pid == 0)
{ //child code
if(num_process == 0){
printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str);
write(fd[1],str,strlen(str));
}
if(num_process == 1){
printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str2);
write(fd[1],str2,strlen(str2));
}
if(num_process == 2){
printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str3);
write(fd[1],str3,strlen(str3));
}
exit(0);
}
else{//parent
printf("Im parent %i\n",getpid());
wait(NULL);
}
}
//Creating another child process from parent, this process recieves string sent from
//childs
pid = fork();
if(pid < 0)
{
perror("fork failed");
exit(1);
}
if(pid == 0){//child
printf("The new process %i read fd pipe\n",getpid());
if( read(fd[0],buffer,sizeof(buffer)) <= 0) //read pipe
{
perror("error read");
exit( EXIT_FAILURE );
}
printf("String readed : %s\n",buffer);
}
else{//parent
wait(NULL);
}
return 0;
}
You need to make a number of changes to the code. The parent shouldn't really wait on its children until after they're all launched. Since you create a new pipe for each of the first three children, you need to keep track of which file descriptors are in use. You should use arrays for that, and for the strings to be sent. Neither the read() nor the write() system calls null-terminates strings, and you don't tell it to write a null byte at the end, so you need to tell printf() to print the correct information.
Those changes and sundry others lead to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_CHILDREN 3
int main(void)
{
pid_t pid;
int fd[MAX_CHILDREN][2];
char buffer[100];
const char *str[MAX_CHILDREN] = { "Hello 1", "Hello 2", "Hello 3" };
for (int i = 0; i < MAX_CHILDREN; i++)
{
if (pipe(fd[i]) == -1)
{
perror("pipe Failed");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork failed");
exit(1);
}
if (pid == 0)
{
printf("Child %i (pid= %i) send string %s\n", i + 1, getpid(), str[i]);
write(fd[i][1], str[i], strlen(str[i]));
exit(i + 1);
}
}
pid = fork();
if (pid < 0)
{
perror("fork failed");
exit(1);
}
if (pid == 0)
{
printf("The new process %i read fd pipe\n", getpid());
for (int i = MAX_CHILDREN; i-- > 0; )
{
int nbytes;
if ((nbytes = read(fd[i][0], buffer, sizeof(buffer))) <= 0)
{
perror("error read");
exit(EXIT_FAILURE);
}
printf("String read: %.*s\n", nbytes, buffer);
}
exit(4);
}
int corpse;
int status;
while ((corpse = wait(&status)) >= 0)
printf("child %d exited with status 0x%.4X\n", corpse, status);
return 0;
}
When run, the output might be:
Child 1 (pid= 91027) send string Hello 1
Child 2 (pid= 91028) send string Hello 2
Child 3 (pid= 91029) send string Hello 3
The new process 91030 read fd pipe
String read: Hello 3
String read: Hello 2
String read: Hello 1
child 91027 exited with status 0x0100
child 91028 exited with status 0x0200
child 91029 exited with status 0x0300
child 91030 exited with status 0x0400
I reversed the order of the elements in the reading loop, mainly just for fun. You can use a conventional for (int i = 0; i < MAX_CHILDREN; i++) loop instead if you prefer.
Although it isn't crucial in this program, you aren't closing enough file descriptors in the children or the parent. The parent should close the write ends of the pipes; it isn't going to be using them. The children should close the read ends of the pipes; they aren't going to be using them. Further, the second and third children should close the pipes opened for the first, and the third should close the pipe for the second, as they aren't going to use those, either. If you don't do this and the fourth child looped waiting for EOF (0 bytes returned), it would hang.
Rule of thumb: 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
Note that an alternative design for the program would create a single pipe outside the loop and the children would all write to the same pipe. You'd probably want to add a newline to the message strings so that the results are separate. You'd definitely want to think about looping the read in the fourth child, and you'd need to worry about the pipe being closed properly, and so on. It'd be a worthwhile sub-exercise to code that.

How to distinguish one child process from other child processes

I have an assignment for class and I am confused on this part of the requirements. So we need to make a multi process word counter with n number of processes and n will be an input argument for the program. Each process needs to do their own mini word count of a select portion of the inputted file. So essentially the inputted file will be divided into 1/n parts and split between n processes.
I understand how to fork the processes through a for loop and how to use pipes to send the mini word count from the children processes to the parent process, but I unsure of how to tell a certain process to do a select part of the input file.
Would you use their PID values to check which process they are then assign them their task?
This is my code so far.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MSGLEN 64
#define MESSES 3
int main(){
int fd[2];
pid_t pid;
int result;
//Creating a pipe
result = pipe (fd);
if (result < 0) {
//failure in creating a pipe
perror("pipe error\n");
exit (1);
}
//Creating a child process
for(int i = 0; i < MESSES; i++){
if ((pid = fork()) < 0) {
//failure in creating a child
perror ("fork error\n");
exit(2);
}
if(pid == 0)
break;
}
if (pid == 0) {
// ACTUALLY CHILD PROCESS
char message[MSGLEN];
//Clearing the message
memset (message, 0, sizeof(message));
printf ("Enter a message: ");
//scanf ("%s",message);
fgets (message, 1024, stdin);
close(fd[0]);
//Writing message to the pipe
write(fd[1], message, strlen(message));
close(fd[1]);
close(fd[0]);
exit (0);
}
else {
//Parent Process
char message[MSGLEN];
char *ptr;
long wc;
close(fd[1]);
while (1) {
//Clearing the message buffer
memset (message, 0, sizeof(message));
//Reading message from the pipe
if(read(fd[0], message, sizeof(message)) == 0)
exit(0);
printf("Message entered %s\n",message);
/*
Message entered needs to be in the format of number first space then string for it to work
*/
wc = 0;
wc = strtol(message, &ptr, 10);
printf("The number(unsigned long integer) is %ld\n", wc);
printf("String part is %s", ptr);
}
close(fd[0]);
wait(NULL);
// exit(0);
}
return 0;
}
The key thing to remember when using fork is that the parent and child share the same memory and a copy of everything the parent has is passed to the child. At which point the child has now forked the parents data.
In the code below we're counting how many processes we've created. You could if you wanted use this as an argument in the child ie the nth child gets value n.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define PROCESS_COUNT 50
int main(void) {
pid_t pid;
size_t pid_count = 0;
//pid_t pid_array[PROCESS_COUNT];
for(int i = 0; i < PROCESS_COUNT; i++) {
if ((pid = fork()) < 0) {
perror ("fork error\n");
exit(2);
}
if (pid == 0) {//child
size_t n = 0;
size_t p = getpid();
while(n++ < 2) {
//Next line is illustration purposes only ie I'm taking liberties by
//printing a pid_t value
printf("child %zu has pid_count == %zu\n", p, pid_count);
sleep(1);
}
exit (0);
}
else {
//Count how many process we've created.
pid_count++;
int status;
waitpid( -1, &status, WNOHANG);
}
}
wait(NULL);
return 0;
}
If you want to get really fancy you can use IPC using pipes or shared memory. There are lots of ways to get data from one process to another, sometimes something as simple as temporary files is more than sufficient. For your problem I'd use mmap but it does not need to be that complicated

Pipes between child processes

I wrote a C program that is supposed to create a certain number of child processes, each child process having to change 1 letter from a string. The string and the number of child processes are read from the keyboard.
I want to do it using pipes. It should work like this: The parent changes one letter, then the first child takes the string modified by the parent and changes one more letter. The second child takes the string modified by the first one (2 letters are already changed) and changes one more and so on. I am new to C and am not quite sure how it all works, especially pipes.
Also can the children be linked between them through the pipe, or can they only be linked to the parent and it has to be something like: first child changes a letter, gives the string back to the parent and then the second child reads from there, modifies letter and gives back.
If it's like that, is there any way to make sure that this doesn't happen: Apples becomes AppleD and then AppleX and then AppleQ?
For example:
input:
3 Apples
output:
Applex Appldx Apqldx
My problem is: I don't get any output from the children. Unsure what I'm doing wrong. Help would be much appreciated, thanks in advance!
Here's my code:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
void error(char* msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
char* modify(char msg[])
{
srand(time(NULL));
int pos1=rand()%((int)strlen(msg));
srand(time(NULL));
int pos2=rand()%26;
srand(time(NULL));
int big=rand()%2;
if(big==1)
{
msg[pos1]=(char)(((int)'A')+pos2);
}
else
{
msg[pos1]=(char)(((int)'a')+pos2);
}
return msg;
}
int main(int argc, char *argv[])
{
if(argc!=3)
{
error("Wrong number of arguments\n");
}
int nrch;
nrch=atoi(argv[1]);
char* msg=argv[2];
printf("Parent: erhalten: %s\n", msg);
int i=0;
msg=modify(argv[2]);
printf("Parent: weiter: %s\n", msg);
pid_t pids[10];
int fd[2];
if(pipe(fd) == -1)
{
error("Can't create the pipe");
}
dup2(fd[1], 1);
close(fd[0]);
fprintf(stdout, msg);
/* Start children. */
for (i = 0; i < nrch; ++i)
{
if ((pids[i] = fork()) < 0)
{
error("Can't fork process");
}
else if (pids[i] == 0)
{
dup2(fd[0], 0);
close(fd[1]);
fgets(msg,255,stdin);
printf("child%d: erhalten: %s\n", (i+1), msg);
modify(msg);
printf("child%d: weiter: %s\n", (i+1), msg);
if (pipe(fd) == -1)
{
error("Can’t create the pipe");
}
fprintf(stdout, msg);
dup2(fd[1], 1);
close(fd[0]);
exit(0);
}
}
/* Wait for children to exit. */
int status;
pid_t pid;
while (nrch > 0)
{
pid = wait(&status);
printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status);
--nrch;
}
}
One reason you see no output from the children is that you hook their standard output to the write end of the pipe, so when they write to standard output, it goes into the pipe, not to the screen (or wherever you sent the standard output of the program to originally).
Where the children are not going to execute a program that needs standard input and standard output going to the pipe, don't use I/O redirection. Just write to and read from the correct ends of the pipe.
If you've got multiple children, you probably need a pipe per child, but the parent process will need to do the creating. Your code creates a pipe in the child; that pipe is no use because only the child knows about it. You probably can do it all with one pipe, but it becomes indeterminate which sequence the children will run in. If determinacy is important, use multiple pipe() calls, and at least twice as many close() calls.
Single pipe solution
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static void error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
exit(1);
}
static void modify(char msg[])
{
int pos1 = rand() % ((int)strlen(msg));
int pos2 = rand() % 26;
int big = rand() % 2;
if (big == 1)
msg[pos1] = (char)(((int)'A') + pos2);
else
msg[pos1] = (char)(((int)'a') + pos2);
}
static int read_pipe(int fd, char *buffer, size_t buflen)
{
int nbytes = read(fd, buffer, buflen);
if (nbytes <= 0)
error("Unexpected EOF or error reading pipe");
assert((size_t)nbytes < buflen);
buffer[nbytes] = '\0';
return nbytes;
}
int main(int argc, char *argv[])
{
if (argc != 3)
error("Usage: %s number 'message'", argv[0]);
srand(time(NULL));
int nrch = atoi(argv[1]);
char *msg = argv[2];
size_t len = strlen(msg);
printf("Parent: erhalten: %s\n", msg);
modify(msg);
printf("Parent: weiter: %s\n", msg);
int fd[2];
if (pipe(fd) == -1)
error("Can't create the pipe");
if (write(fd[1], msg, len) != (ssize_t)len)
error("Failed to write to pipe");
/* Start children. */
for (int i = 0; i < nrch; ++i)
{
int pid;
if ((pid = fork()) < 0)
error("Can't fork process");
else if (pid == 0)
{
char buffer[255];
int nbytes = read_pipe(fd[0], buffer, sizeof(buffer));
printf("child%d: erhalten (%d): %s\n", (i + 1), nbytes, buffer);
modify(buffer);
printf("child%d: weiter (%d): %s\n", (i + 1), nbytes, buffer);
write(fd[1], buffer, nbytes);
exit(0);
}
else
printf("Random: %d\n", rand());
}
/* Wait for children to exit. */
while (nrch > 0)
{
int status;
pid_t pid = wait(&status);
printf("Child with PID %ld exited with status 0x%.4X.\n", (long)pid, status);
--nrch;
}
char buffer[255];
int nbytes = read_pipe(fd[0], buffer, sizeof(buffer));
printf("Parent: weiter (%d): %s\n", nbytes, buffer);
return 0;
}
Example output
Code in file p1.c:
$ make p1 && ./p1 4 "Absolutely nothing to do with me"
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror p1.c -o p1
Parent: erhalten: Absolutely nothing to do with me
Parent: weiter: AbsolutEly nothing to do with me
Random: 1120753102
child1: erhalten (32): AbsolutEly nothing to do with me
Random: 918317477
child1: weiter (32): AbsolutEly notzing to do with me
child2: erhalten (32): AbsolutEly notzing to do with me
child2: weiter (32): AbsolwtEly notzing to do with me
Random: 196864950
child3: erhalten (32): AbsolwtEly notzing to do with me
child3: weiter (32): AbsolwtEly notzing to ao with me
Random: 1584398270
Child with PID 42928 exited with status 0x0000.
Child with PID 42927 exited with status 0x0000.
Child with PID 42926 exited with status 0x0000.
child4: erhalten (32): AbsolwtEly notzing to ao with me
child4: weiter (32): AbsolwtEly notzing to ao with Ue
Child with PID 42929 exited with status 0x0000.
Parent: weiter (32): AbsolwtEly notzing to ao with Ue
$
Note the stray use of rand() in the loop. It makes sure the children change different letters in the message. Without that, they all end up changing the same 'random' letter in the same 'random' position in the message.
You can create a multi-pipe solution if you wish. I got what appeared to be deterministic behaviour from the single-pipe solution, though there is no guarantee of the sequencing. If, for example, each child waited for a random delay using nanosleep() or equivalent:
struct timespec nap = { .tv_sec = 0, .tv_nsec = (rand() % 1000) * 1000000 };
nanosleep(&nap, 0);
then you get an arbitrary sequence in the child processing. For example:
Parent: erhalten: Absolutely nothing to do with me
Parent: weiter: Absolutely nothinglto do with me
Random: 2028074573
Random: 988903227
Random: 1120592056
Random: 359101002
child4: erhalten (32): Absolutely nothinglto do with me
child4: weiter (32): vbsolutely nothinglto do with me
Child with PID 43008 exited with status 0x0000.
child3: erhalten (32): vbsolutely nothinglto do with me
child3: weiter (32): vbsolutelyGnothinglto do with me
Child with PID 43007 exited with status 0x0000.
child2: erhalten (32): vbsolutelyGnothinglto do with me
child2: weiter (32): vbsolutelyGnothinglto doawith me
Child with PID 43006 exited with status 0x0000.
child1: erhalten (32): vbsolutelyGnothinglto doawith me
child1: weiter (32): vbsolutelyGnothinglto doawimh me
Child with PID 43005 exited with status 0x0000.
Parent: weiter (32): vbsolutelyGnothinglto doawimh me
Tried to change your code acc to below, not sure if this is exectly what you want.
Anyhow the children are running...
int main(int argc, char *argv[])
{
if (argc != 3)
{
error("Wrong number of arguments\n");
}
int nrch;
nrch = atoi(argv[1]);
char *msg = argv[2];
printf("Parent: erhalten: %s\n", msg);
int i = 0;
msg = modify(argv[2]);
printf("Parent: weiter: %s\n", msg);
pid_t pids[10];
int fd[2];
/* Start children. */
for (i = 0; i < nrch; ++i)
{
if ((pids[i] = fork()) < 0)
{
error("Can't fork process");
}
if (pipe(fd) == -1)
{
error("Can't create the pipe");
}
// printf ( " pids[i] %d , i %d \n", pids[i] , i);
if (pids[i] == 0)
{
if (dup2(fd[0], 0) == -1)
{
error("Can't dup2 (A)");
}
close(fd[1]);
fgets(msg, 255, stdin);
printf("child%d: erhalten: %s\n", (i + 1), msg);
modify(msg);
printf("child%d: weiter: %s\n", (i + 1), msg);
fprintf(stdout, msg);
}
else
{
// printf("in else i= %d \n",i);
if (dup2(fd[1], 0) == -1)
{
error("Can't dup2 (B)");
}
close(fd[0])
exit(0);
}
}
/* Wait for children to exit. */
int status;
pid_t pid;
while (nrch > 0)
{
pid = wait(&status);
if (pid > -1)
printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status);
--nrch;
}
return 0;
}

Looping an unnamed pipe in C

Ok, I searched for this one but couldn't find it. Apologize if it's already been answered before.
Basically I have a program for class that creates two unnamed pipes and uses them to communicate between the parent and child process. The command is passed from the parent to the child, where the child executes it and returns a Success/Error message to the parent. Parent then prints out the Success/Error message. Easy enough, I have that working. Problem is now I need to loop it until the user gives the "exit" command. I think I need a while loop, but after experimenting with placement the program still only runs once and then exits. Here's what I have. Hopefully this makes sense, I left out the processing portion of the code since that part works (like I said, for a class) but if there's anything that doesn't make sense I'll clarify. Thanks in advance for any help.
while (strcmp(cmd,"exit") != 0)
{
/* Create Pipe P to pass command from the
parent process to the child process and check for errors.*/
pipe(p);
/*Create Pipe Q to pass command from the
child process to the parent process and check for errors. */
pipe(q);
/* Create child process */
pid = fork();
switch(pid){
case -1: /* fork failed */
perror("main: fork");
exit(1);
case 0: /* Child process */
/*****************************************
Stuff being executed in the child process
*****************************************/
default: /* Parent process */
printf ("Choose from the following list of commands.\n");
printf ("display\n");
printf ("chars\n");
printf ("lines\n");
printf ("words\n");
printf ("find\n");
printf ("exit\n");
fgets (cmd,10,stdin);
if ((c = strchr(cmd, '\n')) != NULL)
{
*c = '\0';
}
/**********************************
Pipes being opened and closed for
communication between parent and child
**************************************/
break;
}
return 0;
}
}
You need to create the child before you enter the loop.
You also need to be a lot more careful with the plumbing. The main (parent) process must close the ends of the pipes that it won't use, and the child likewise (noting that the child closes the opposite ends from the parent). Of course, if the child is reading standard input and writing on standard output, then you have to arrange for the pipes to be duplicated to the correct descriptors, and then the child closes all the descriptors returned by the pipe() call.
Try this for size - you'd have to expand be_childish() to do the real work...
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
static void be_childish(int p[2], int q[2]);
static void be_parental(int p[2], int q[2]);
static void err_exit(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int p[2]; /* Pipe to child */
int q[2]; /* Pipe to parent */
pid_t pid;
if (pipe(p) != 0)
err_exit("Failed to create pipe 1");
if (pipe(q) != 0)
err_exit("Failed to create pipe 2");
if ((pid = fork()) < 0)
err_exit("Failed to create child process");
else if (pid == 0)
be_childish(p, q);
else
be_parental(p, q);
return(0);
}
static int prompt(char *buffer, size_t buflen)
{
char *c;
printf("Choose from the following list of commands.\n");
printf("display\n");
printf("chars\n");
printf("lines\n");
printf("words\n");
printf("find\n");
printf("exit\n");
if (fgets(buffer, buflen, stdin) == 0)
return EOF;
if ((c = strchr(buffer, '\n')) != NULL)
*c = '\0';
if (strcmp(buffer, "exit") == 0)
return EOF;
return 0;
}
static void be_parental(int p[2], int q[2])
{
char cmd[10] = "";
if (close(p[0]) != 0 || close(q[1]) != 0)
err_exit("Parent: failed to close pipe");
while (prompt(cmd, sizeof(cmd)) != EOF)
{
char buffer[4096];
ssize_t nbytes;
if (write(p[1], cmd, strlen(cmd)) != (ssize_t)strlen(cmd))
err_exit("Write to child failed");
if ((nbytes = read(q[0], buffer, sizeof(buffer))) < 0)
err_exit("Read from child failed");
if (nbytes == 0)
return;
printf("%s\n", buffer);
}
}
static void be_childish(int p[2], int q[2])
{
char cmd[10] = "";
ssize_t nbytes;
if (close(p[1]) != 0 || close(q[0]) != 0)
err_exit("Child: failed to close pipe");
while ((nbytes = read(p[0], cmd, sizeof(cmd))) > 0)
{
char buffer[4096];
cmd[nbytes] = '\0';
/* Process command */
strcpy(buffer, "Response from child: ");
strcat(buffer, cmd);
if (write(q[1], buffer, strlen(buffer)) != (ssize_t)strlen(buffer))
err_exit("Write to parent failed");
}
}

Resources