Using Fork() and Exec() for Child Processes in Ubuntu - c++

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.

Related

Parent process exits twice using fork() execv() in c++

I have a multiprocessing application that works well, except the parent process seems to exit twice.
I left out some of the code for simplification. Basically, I use libcurl (I wrote my own abstraction layer for it) to get JSON data from a server (left the code for this out) and then the simdjson library to iterate through it and run worker processes where required.
At the end I wait for all child processes (in the parent process) to terminate before printing "done". I can see however, that my program is printing "done" twice. I presume once after it's done in the for loop to create all the worker processes and then again once the last child returns. At least that is what I can see from the output on the console, as the child processes print to the console as well. However, given that I use if (pid_fork > 0), i.e. I must be in the parent process, any subsequent code should be executed only once. What am I doing wrong?
#include <iostream>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "simdjson.h"
int main (int argc, char *argv[])
{
/* some other code */
pid_t pid_fork;
std::vector<int> v_pid;
// loop through json
for (simdjson::dom::element mq_item : json_mq_items)
{
pid_fork = fork();
if (pid_fork == -1)
{
std::cout << "error: could not fork process" << std::endl;
return EXIT_FAILURE;
} else if (pid_fork > 1) // parent process
{
v_pid.push_back(pid_fork);
}
else // child process (pid_fork == 0)
{
char *argv[] = { (char*)(std::string("foo")), NULL };
if (execv((static_cast<std::string>("./foo")).c_str(), argv) == -1)
{
std::cout << "could not load child" << std::endl;
return EXIT_FAILURE;
}
}
}
// in parent process only
if (pid_fork > 0)
{
// Wait for all child processes to terminate
for (size_t i = 0; i < v_pid.size(); i++)
{
while (waitpid(v_pid[i], NULL, 0) > 0);
}
/* some other code */
std::cout << "done" << std::endl;
return EXIT_SUCCESS;
}
}

How to avoid duplicate input on stdin when using 2-process pipeline with stdin and stdout redirect

I am intending to set up a pipeline between two processes: parent and child. The parent forks the child and uses execve to replace its image with that of a specified process.
The parent reads from stdin via std::getline(std::cin, input_line).
The child writes to the stdout via std::cout << output_line.
I am looking to setup a pipe and redirect the output of the child to the input of the parent.
The problem is that the parent receives each input (where each input is a number output by the child on stdout) twice. I would like to fix this issue but I don't understand why it is happening.
Code is compiled with g++ 7.4.0 and C++11 standard version.
Child is compiled to a binary called 'p1'.
Parent code:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
char *
const p1argv[] = {
(char * )
"./p1",
nullptr
};
char *
const p1envp[] = {
(char * ) nullptr
};
int main(int argc, char ** argv) {
pid_t p1id;
int p1fd[2];
pipe(p1fd);
if (p1id = fork() == 0) {
close(p1fd[0]);
dup2(p1fd[1], STDOUT_FILENO);
execve(argv[0], p1argv, p1envp);
perror("Error: failed to execve ./p1.");
} else {
dup2(p1fd[0], STDIN_FILENO);
close(p1fd[1]);
std::string line;
while (std::getline(std::cin, line)) {
std::cout << "d(" << line << ")" << std::endl;
}
int status;
waitpid(p1id, & status, 0);
close(p1fd[0]);
}
}
Child code:
#include <iostream>
#include <thread>
int main(int argc, char** argv) {
long it = 0;
while(true) {
it += 1;
std::cout << std::to_string(it) << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
The actual output for the sample code is:
d(d(1))
d(d(2))
...
The expected output is:
d(1)
d(2)
...
The problem is that this line:
execve(argv[0], p1argv, p1envp);
Is re-executing the main parent program, because that is what the content of argv[0] is at this point. I think you want to find some way to specify "./p1" there.

Inverting a string using `fork()`

I'm trying to invert a string in C++ using fork(), such that each process prints at most one character. My thinking is that after printing each character, I fork into a new process, end the parent process, and continue. Here is my code:
#include <string>
#include <iostream>
#include <unistd.h>
/*
Recursively print one character at a time,
each in a separate process.
*/
void print_char(std::string str, int index, pid_t pid)
{
/*
If this is the same process,
or the beginning of the string has been reached, quit.
*/
if (pid != 0 || index <= -1)
return;
std::cout << str[index];
if (index == 0)
{
std::cout << std::endl;
return;
}
print_char(str, index-1, fork());
}
int main(int argc, char** argv)
{
std::string str(argv[1]);
print_char(str, str.length()-1, 0);
}
However, when testing the code with the argument "hey", it prints "yeheyy". My understanding of fork() is that it creates a duplicate process with a copy of the memory space, and whenever I mentally "walk through" the code it seems like it should work, but I cannot figure out where my logic is failing.
It seems, your code is OK, but you have trouble with cout.
try change only the output line
std::cout << str[index];
with
std::cout << str[index] << std::flush;
tried it and worked for me.

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;
}

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

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.