Why does this code stop running when it hits the pipe? - c++

When this program runs it goes through the loop in the parent then switches to the child when it writes to the pipe. In the child the pipe that reads just causes the program to stop.
Current example output:
Parent 4741 14087 (only this line when 5 more lines are expected)
Expected output(with randomly generated numbers):
Parent 4741 14087
Child 4740 47082
Parent 4741 11345
Child 4740 99017
Parent 4741 96744
Child 4740 98653
(when given the variable 3 and the last number is a randomly generated number)
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <ctime>
using namespace std;
int main (int argc, char *argv[]) {
int pid = fork(), temp, randNum, count, pipeName[2], pipeName2[2];
string conver;
pipe(pipeName);
conver = argv[1];
temp = atoi(conver.c_str());
char letter;
if (pid == 0) { //child
srand((unsigned)time(NULL) * getpid() );
//closing unused pipes
close(pipeName2[1]);
close(pipeName[0]);
//loop to switch between processes
for(int i=0; i<temp; i++) {
count = read(pipeName2[0], &letter, 20);
randNum = rand();
cout << "Child " << getpid() << " " << randNum << endl;
write(pipeName[1], "x", 1);
}
close(pipeName2[0]);
close(pipeName[1]);
}
else { //parent
srand((unsigned)time(NULL) * getpid() );
pipe(pipeName2);
//closing unused pipes
close(pipeName2[0]);
close(pipeName[1]);
//loop to switch between processes
for(int i=0; i<temp; i++) {
if(i != 0)
count = read(pipeName[0], &letter, 20);
randNum = rand();
cout << "Parent " << getpid() << " " << randNum << endl;
write(pipeName2[1], "x", 1);
}
close(pipeName[0]);
close(pipeName2[1]);
}
}
The program ends when it hits the read from pipe line in the child.

Your principal mistake is fork()ing before you initialize the pipes. Both parent and child thus have their own private (not shared via fd inheritance) pipe pair named pipeName, and only the parent initializes pipeName2 with pipe fds.
For the parent, there's simply no data to read behind pipeName[0]. For the child ... who knows what fd it is writing to in pipeName2[1]? If you're lucky that fails with EBADF.
So, first pipe() twice, and then fork(), and see if that improves things.

Related

Using Fork() and Exec() for Child Processes in Ubuntu

I am new to C++ and Linux and I am confused on how to correctly pass an integer parameter using execlp() to a child class. I tried following the parameter requirements for this system call, however, the argument is not passing the correct value when I am executing the program. The char conversions is what is making me confused.
In the programs below, the parent accepts gender name pairs from the terminal. Next, it uses the fork() and exec() system calls where it passes the child number, gender, and name to the child program. The child program will output the statement. The output for the child numbers is off (should be: 1,2,3,4,...ect.). However, the output value is blank. Is it because of how I am initializing the argument in the execlp() system call?
Below is my code for the parent program - parent.cc:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
int main (int argc, char* argv[])
{
int i, num = 0;
/* Holds the value of the number of pairs */
int pairs = (argc - 1) / 2;
pid_t pid;
cout << "I have " << pairs << " children." << endl;
/* Perform all child process depending on the number of name-gender pairs*/
for (i = 1; i <= argc-1; i+=2)
{
pid = fork();
/* Child process */
if (pid == 0)
{
char n[] = {char(num++)};
execlp("./c",n,argv[i],argv[i+1], NULL);
}
/* Parent will wait until all child processes finish */
wait(NULL);
}
cout << "All child process terminated. Parent exits." << endl;
/* Exits program */
return 0;
}
Here is my child program- child.cc
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char* argv[])
{
/* When child process starts, it will print out this statement */
cout << "Child # " << argv[0] << ": I am a " << argv[1] << ", and my name is " << argv[2] << endl;
exit(0);
}
Here is my output in the terminal:
g++ -o parent.cc
g++ -o c child.cc
./p boy Mark girl Emily boy Daniel girl Hailey
I have 4 children.
Child # : I am a boy, and my name is Mark
Child # : I am a girl, and my name is Emily
Child # : I am a boy, and my name is Daniel
Child # : I am a girl, and my Hailey
All child process terminated. Parent exits.
There's two and a half issues:
You are not correctly converting an integer to a string, so the value is wrong
You are incrementing the number in the child process, so the next child process won't see the update
argv[0] is conventionally the program name, and failing to follow this convention means that e.g. ./c 1 boy Mark will not work in a shell, and that the child process can't easily be substituted for something written in a different language.
Here's the updated parent process:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
int main (int argc, char* argv[])
{
int i, num = 0;
/* Holds the value of the number of pairs */
int pairs = (argc - 1) / 2;
pid_t pid;
cout << "I have " << pairs << " children." << endl;
/* Perform all child process depending on the number of name-gender pairs*/
for (i = 1; i <= argc-1; i+=2)
{
pid = fork();
/* Child process */
if (pid == 0)
{
string str = to_string(num);
execlp("./c",
"./c", // Conventionally the first argument is the program name
str.c_str(), // Pass in the correctly formatted number
argv[i],argv[i+1], NULL);
}
// Increment in the parent process so that the change is not lost
num++;
/* Parent will wait until all child processes finish */
wait(NULL);
}
cout << "All child process terminated. Parent exits." << endl;
/* Exits program */
return 0;
}
Accordingly, the child should access argv[1], 2, and 3, and not 0, 1 and 2.

C++ Execute one child process iteration after another process

I want to create two child processes with some iterations in them so that iteration l in process X always executes after iteration l+1 in process Y.
#include <stdio.h>
#include <unistd.h>
#include <bits/stdc++.h>
#include <sys/wait.h>
#include <semaphore.h>
using namespace std;
sem_t mut;
int x = 2;
int main()
{
int cpX = fork();
sem_init(&mut, 1, 1);
if (cpX == 0)
{
//child A code
for (int i = 0; i < 10; i++)
{
sem_wait(&mut);
cout << "Inside X:" << getpid() << ", " << i << '\n';
sleep(rand() % 5);
}
exit(0);
}
else
{
int cpY = fork();
if (cpY == 0)
{
//child B code
for (int i = 0; i < 10; i++)
{
cout << "Inside Y:" << getpid() << ", " << i << '\n';
sleep(rand() % 5);
sem_post(&mut);
}
//sem_wait(&mut);
exit(0);
}
else
{
//sleep(50);
//wait(NULL);
//wait(NULL);
exit(0);
// wait(NULL);
}
}
}
However here, X executes once, and then Y starts executing and X never executes again. Why is this happening?
The parent/child during a fork is doing a copy so they are not referring to the same semaphore object. Hence, mut is not shared as what you assumed. Your snippet is near correct you just need to do minor changes to work as you expect.
Since you are using an unnamed semaphore, you need to instantiate the semaphore object in a shared area:
#include <sys/mman.h>
sem_t *mut = (sem_t*)mmap(NULL, sizeof(*mut), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS, -1, 0);
sem_init(mut, 1, 1);
With this minor adjustments, your snippet above now works as expected as they are now referring to the same semaphore object.
Also, some tips: sem_post is an unblocking call, whereas sem_wait is a blocking call. You need to be also aware that a signal sent by sem_post could get lost when nobody is waiting on the semaphore during that moment.
The best reference of this kinds of things is Unix Network Programming: Interprocess Communication by Richard Stevens

How do I send data back along the second pipe in the correct format?

I have been struggling for two days to attempt to fix this final bug in my code, but can't seem to find the error. The code is supposes to(in order):
Receive a string from the user (in this case me)
Create a child process
Send the string to the child process
Rework the string so that every word starts with a capital letter
Send the string back to the parent with the changes
Display the string
The code runs fine until the parent read. An example output is:
Input: "helLO tHerE"
Parent writes "helLO tHerE"
Child reads "helLO tHerE"
Child writes "Hello There"
Parent reads ##$%^$#%^&* - or some other such non-standard characters, then displays error -
double free or corruption (out): 0x00007ffeeebb2690 ***
Below is my code:
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <algorithm>
using namespace std;
int main(){
int fd[2];
int pfc[2];
int status = 0;
string val = "";
if(pipe(fd) == -1 || pipe(pfc) == -1) fprintf(stderr,"Pipe failed");
pid_t pid = fork();
// fork() returns 0 for child process, child-pid for parent process.
if (pid == 0){ // child: reading only, so close the write-descriptor
string writeval = "";
close(fd[1]);
// now read the data (will block)
read(fd[0], &val, sizeof(val));
cout << "Child reads " << val.c_str() << endl;
string temp = " " + val;
transform(temp.begin(), temp.end(), temp.begin(), ::tolower);
for(size_t i = 1; i < temp.length(); i++){
if(!isspace(temp[i]) && isspace(temp[i-1])){
temp[i] = toupper(temp[i]);
}
}
writeval = temp.substr(1, temp.length() - 1);
// close the read-descriptor
close(fd[0]);
close(pfc[0]);
cout << "Child writes " << writeval.c_str() << endl;
write(pfc[1], &writeval, sizeof(writeval));
close(pfc[1]);
exit(0);
}
else{
string readval = "";
string temp ="";
// parent: writing only, so close read-descriptor.
close(fd[0]);
// send the value on the write-descriptor.
while(getline(cin, temp)){
val += temp;
}
write(fd[1], &val, sizeof(val));
cout << "Parent writes " << val << endl;
// close the write descriptor
close(fd[1]);
//wait(&status);
close(pfc[1]);
read(pfc[0], &readval, sizeof(readval));
cout << "Parent reads " << readval << endl;
close(pfc[0]);
}
return 0;
}
So the answer is simple. In the child process I was passing the memory location of writeval in the write back to the parent method, but in the parent process I was trying to read from the memory location of readval. This is fixed by changing them to be the same variable, outside of the if/else calls, like was done with the variable val.
See here for more details on why this is a problem.

create multiple processes with fork() that communicate with pipe without using exit() or wait()

wait(), exit() and signals are forbidden
only pipes allowed
A user gives an integer positive number-N and N-processes are created, father creates a child, that child becomes a father and creates another child and so on. Each one of the first processes (N-1) should wait to finish its process-child first and then itself. The initial process should print "1-My Process ID: ", the next process that's been created the number "2 My process ID: and my father's ID:" and so on.
my code. i don't have wait or exit instead i use return(-1).
but i didn't managed to print accordingly the numbers 1 my process id..., 2 my process id..., 3 my process id... and so on.
any ideas?
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
/* Read characters from the pipe and echo them to stdout. */
void read_from_pipe (int file)
{
FILE *stream;
int c;
stream = fdopen (file, "r");
while ((c = fgetc (stream)) != EOF)
putchar (c);
fclose (stream);
}
/* Write some random text to the pipe. */
void write_to_pipe (int file)
{
FILE *stream;
stream = fdopen (file, "w");
fprintf (stream, "\n");
fprintf (stream, " ");
fclose (stream);
}
int main (void)
{
pid_t pid;
int mypipe[2];
int j = 1;
int i;
cout << "\nassume father is by default the first process\n" << "Please enter how child-processes you want: ";
cin >> i;
for( ; j < i; j++)
{
/* Create the pipe. */
if (pipe (mypipe))
{
fprintf (stderr, "Pipe failed.\n");
return (-1);
}
/* Create the child process. */
pid = fork ();
if (pid == (pid_t) 0)
{
/* This is the child process. Close other end first. */
pid = getpid();
close (mypipe[1]);
read_from_pipe (mypipe[0]);
printf("Child's ID: %d\n",pid);
sleep(0);
}
else if (pid > (pid_t) 0)
{
/* This is the parent process. Close other end first. */
pid = getpid();
close (mypipe[0]);
write_to_pipe (mypipe[1]);
printf("Dad's ID: %d\n",pid);
sleep(0);
}
else
{
/* The fork failed. */
fprintf (stderr, "Fork failed.\n");
return (-1);
}
}//end for
//close (mypipe[0]);
//write_to_pipe (mypipe[1]);
// printf("Dad's ID: %d\n",pid);
return (-1);
}// EOP
Recursion could be simpler than iteration because you want each child to in turn create another child. The trick to avoid wait is to have each parent to read on the read end of the pipe, and to have the child to close the write end just before returning without writing anything. Because the read will be blocked until either something has been written or the other end is closed.
You cannot be sure of the order in which the processes will actually end because you do not call wait, but you are sure that a parent cannot end before its childs has terminated its job.
Code could be:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
using std::cout;
using std::cin;
using std::cerr;
using std::endl;
int start_child(int i, int j) {
int my_pipe[2];
pid_t parent_pid, pid;
/* Create the pipe. */
if (pipe (my_pipe))
{
cerr << "Pipe failed." << endl;
return (-1);
}
/* Create the child process. */
parent_pid = getpid();
pid = fork ();
if (pid == (pid_t) 0) {
/* child */
pid = getpid();
close(my_pipe[0]);
cout << "I'm child " << j << "- my pid is " << pid <<
" - my parent's pid is " << parent_pid << endl;
if (i > 1) start_child(i - 1, j + 1);
if (pid == getpid()) cout << "End of child "<< j << endl;
close(my_pipe[1]);
}
else if (pid == (pid_t) -1) {
perror("forking");
close(my_pipe[0]);
close(my_pipe[1]);
return -1;
}
else {
/* parent */
close(my_pipe[1]);
char buf[2];
read(my_pipe[0], buf, 2); // wait for the child to close its pipe end
close(my_pipe[0]);
}
return 0;
}
int main (void)
{
pid_t pid = getpid();
int i;
cout << "\nassume father is by default the first process\n" << "Please enter how child-processes you want: ";
cin >> i;
cout << "I'm parent - my pid is " << pid << endl;
int cr = start_child(i, 1);
if (pid == getpid()) cout << "End of parent" << endl;
return cr;
}// EOP

C++ function running twice but only called once

I am learning C++ [Java background fwiw] and trying to write a UNIX shell as a project. I am running into a funny little problem with tokenizing the input for execution. The tok function is getting called twice and I'm not sure why. My current test code is the following:
#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
void tok(string, char**);
int main(){
const char* EXIT = "exit";
string input;
cout << "shell>> ";
getline(cin, input);
pid_t pid = fork();
char* args[64]; //arbitrary size, 64 possible whitespace-delimited tokens in command
tok(input, args);
return 0;
}
//copied from http://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
void tok(string inStr, char** args){
int last = 0, next = 0, i = 0;
while( (next = inStr.find(' ', last)) != -1){
cout << i++ << ": " << inStr.substr(last, next-last) << endl;
*args++ = strdup(inStr.substr(last, next-last).c_str());
last = next + 1;
}
cout << i++ << ": " << inStr.substr(last) << endl;
*args++ = strdup(inStr.substr(last).c_str());
*args = '\0';
cout << "done tokenizing..." << endl;
}
My output when I actually run the program is:
$ ./a.out
shell>> ls -l
0: ls
1: -l
done tokenizing...
0: ls
1: -l
done tokenizing...
I'm not sure why it would do that. Can anyone guide me in the right direction please? Thank you
The fork function returns twice, once in the original process and once in the newly-created, forked process. Both of those processes then call tok.
There doesn't seem to be any clear reason why you called fork. So the fix may be as simple as eliminating the call to fork.
When you call fork, you create two processes. Each process has nearly the exact same state except for the respective pid_t you receive. If that value is greater than 0, then you are in the parent process (main), and otherwise you are in the child (or fork failed).
Without performing a check on the returned pid_t, both processes will call tok, resulting in the double call behavior you witnessed.
Hide the call behind a check on pid like so:
pid_t pid = fork();
if (pid > 0) // have parent process call tok
{
char* args[64]; //arbitrary size, 64 possible whitespace-delimited tokens in command
tok(input, args);
}
To see what else parent and child processes have in common (or not): check the docs
following code may work fine
#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
void tok(string, char**);
int main(){
const char* EXIT = "exit";
string input;
cout << "shell>> ";
getline(cin, input);
// pid_t pid = fork();
char* args[64];
tok(input, args);
return 0;
}
void tok(string inStr, char** args){
int last = 0, next = 0, i = 0;
while( (next = inStr.find(' ', last)) != -1){
cout << i++ << ": " << inStr.substr(last, next-last) << endl;
*args++ = strdup(inStr.substr(last, next-last).c_str());
last = next + 1;
}
cout << i++ << ": " << inStr.substr(last) << endl;
*args++ = strdup(inStr.substr(last).c_str());
*args = '\0';
cout << "done tokenizing..." << endl;
}