Why does the child of this class does not print? - c++

I have made a class that is supposed to handle a subprocess but when I put a test print inside nothing happens, can someone tell me why ?
I have tried different types of print and combinations of fflush but it didn't solve the problem.
Here are my files:
Kitchen.cpp
Kitchen::Kitchen()
{
int wstatus;
this->_pid = fork();
if (this->_pid == 0) {
std::cout << "child: " << this->_pid << std::endl;
} else if (this->_pid == -1) {
throw("No child");
} else {
std::cout << "parent: " << this->_pid << std::endl;
waitpid(this->_pid, &wstatus, 1);
}
}
Kitchen::~Kitchen()
{
if (this->_pid > 0)
kill(this->_pid, SIGKILL);
}
Kitchen.hpp
#pragma once
#include <signal.h>
#include <unistd.h>
class Kitchen {
public:
Kitchen();
~Kitchen();
private:
pid_t _pid;
};
Reception.cpp
int Reception::run_shell(void)
{
this->_kitchens.push_back(Kitchen());
return (0);
}
Reception.hpp
#pragma once
#include <iostream>
#include <sstream>
#include "Kitchen.hpp"
class Reception
{
public:
Reception();
~Reception();
void repart(std::vector<Package> packed_order);
bool is_order(std::string input);
std::vector<Package> pack(std::string input);
int run_shell(void);
private:
std::vector<Kitchen> _kitchens;
};
main.cpp
int main(void)
{
Reception reception;
return (reception.run_shell());
}
Right now only the parent prints where as I would like both process to print.
Note that this code works outside of the class.

is it better now ?
Yes, better.
I copied your code to my Lubuntu 18.04, and using g++ 7.3.0-27, got the thing to compile.
The copied code on my system reproduced your error.
Hmmm.
So, I went looking for, and quickly found, my most recent experiment with fork. I do not understand why it works, and yours does not, they look similar enough to me.
I use a switch instead of your nested if-then-else's ... perhaps there exists a glitch in the nested if-then-else's? a faulty char transfer? But I doubt it.
So ... for pragmatic reasons, I (minimally?) changed your code to match my example.
Perhaps you can ask a better question as to why this seems to work, and in your version does not.
Hope this helps:
#include "../../bag/src/dtb_chrono.hh"
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
#include <iostream>
using std::cout, std::cerr, std::flush, std::endl;
#include <string>
using std::string;
#include <thread>
using std::this_thread::sleep_for;
#include <vector>
using std::vector;
#include <cstring>
using std::strerror;
#include <unistd.h> // fork
#include <sys/wait.h> // waitpid
#include <cassert>
class Kitchen
{
pid_t child_pid;
time_t pt0; // child start time 0
time_t ct0; // parent start time 0
public:
Kitchen()
{
pt0 = time(0) + 2;
ct0 = time(0) + 1;
// On success, the PID of the child process is returned in the
// parent, and 0 is returned in the child.
//
// On failure, -1 is returned in the parent, no child process is
// created, and errno is set appropriately.
child_pid = fork();
switch (child_pid)
{
case -1: { errnoExit (errno, "\n fork fail: ", -12); } break;
case 0: // child
{
std::cout << "\n i am child: " << child_pid << endl;
ChildProcess();
}
break;
default: // parent
{
std::cout << "\n i am parent, child_pid: " << child_pid << flush;
ParentProcess();
}
} // switch(child_pid)
} // Kitchen
~Kitchen()
{
if (child_pid > 0)
{ }; // { kill(child_pid, SIGKILL)};
}
void ChildProcess(void)
{
int i = 0;
do {
i += 1;
cout << "\n child " << i;
std::this_thread::sleep_for(100ms);
if (time(0) > ct0) break;
}while (true);
cout << "\n*** Child complete ***" << '\n';
}
void ParentProcess(void)
{
int i = 0;
do {
i += 1;
cout << "\n parent " << i ;
std::this_thread::sleep_for(100ms);
if (time(0) > pt0) break;
}while (true);
int wstatus;
waitpid(child_pid, &wstatus, 1); // see output -
// waitpid not effective because parent runs longer than child
// but causes no harm ...
//
// TBD - when parent run is shorter than child?
// appears that parent end halts child?
cout << "\n*** Parent complete ***" << '\n';
}
private:
void errnoExit(int err_no, const string message, int id) {
assert(0 != err_no); cerr << message << strerror(err_no);
assert(id < 0); exit(id); }
}; // class Kitchen
class Reception
{
public:
Reception() = default;
~Reception() = default;
int operator()(int argc, char* argv[]) { return run_shell(argc, argv); }
//void repart(std::vector<Package> packed_order);
//bool is_order(std::string input);
//std::vector<Package> pack(std::string input);
int run_shell(int /*argc*/, char** /*argv[]*/)
{
_kitchens.push_back(Kitchen());
return (0);
}
private:
vector<Kitchen> _kitchens;
}; // class Reception
int main(int argc, char* argv[]) { return Reception()(argc, argv); }
Typical Output:
i am parent, child_pid: 6727
i am child: 0
parent 1
child 1
parent 2
child 2
parent 3
child 3
parent 4
child 4
parent 5
child 5
parent 6
child 6
parent 7
child 7
parent 8
child 8
parent 9
child 9
parent 10
child 10
parent 11
child 11
parent 12
child 12
parent 13
child 13
parent 14
child 14
parent 15
child 15
parent 16
child 16
parent 17
child 17
parent 18
child 18
*** Child complete ***
parent 19
parent 20
parent 21
parent 22
parent 23
parent 24
parent 25
parent 26
parent 27
parent 28
*** Parent complete ***

The last else lacks curly braces, waitpid(this->_pid, wstatus, 1); is executed by both child and parent...

The parent process needs to be told to wait for any child processes to complete. I noticed your final else condition is missing the parentheses so only the one line after the else will be executed.
The other issue is the pointer wstatus. Because it's a pointer that isn't initialized, I'm not sure which behavior will be expressed. Change it to an int and in the waitpid call, use &wstatus.

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

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.

Creating 3 children processes and exiting them after a specified number of seconds

image for what output is supposed to look like:My problem is that I need to write a program that will accept the names of 3 processes as command-line arguments. Each of these processes will run for as many seconds as:(PID%10)*3+5 and terminate. After those 3 children terminated, the parent process
will reschedule each child. When all children have been rescheduled 3 times, the parent will terminate. I have used fork to create the three children but am struggling with getting them to exit with that specific criteria?
using namespace std;
int main(){
int i;
int pid;
for(i=0;i<3;i++) // loop will run n times (n=3)
{
if(fork() == 0)
{
pid = getpid();
cout << "Process p" << i+1 << " pid:" << pid << " Started..." << endl;
exit(0);
}
}
for(int i=0;i<5;i++) // loop will run n times (n=3)
wait(NULL);
}
You can use sigtimedwait to wait for SIGCHLD or timeout.
Working example:
#include <cstdio>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
template<class... Args>
void start_child(unsigned max_runtime_sec, Args... args) {
// Block SIGCHLD.
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, nullptr);
// Enable SIGCHLD.
signal(SIGCHLD, [](int){});
pid_t child_pid = fork();
switch(child_pid) {
case -1:
std::abort();
case 0: {
// Child process.
execl(args..., nullptr);
abort(); // never get here.
}
default: {
// paren process.
timespec timeout = {};
timeout.tv_sec = max_runtime_sec;
siginfo_t info = {};
int rc = sigtimedwait(&set, nullptr, &timeout);
if(SIGCHLD == rc) {
std::printf("child %u terminated in time with return code %d.\n", static_cast<unsigned>(child_pid), info.si_status);
}
else {
kill(child_pid, SIGTERM);
sigwaitinfo(&set, &info);
std::printf("child %u terminated on timeout with return code %d.\n", static_cast<unsigned>(child_pid), info.si_status);
}
}
}
}
int main() {
start_child(2, "/bin/sleep", "/bin/sleep", "10");
start_child(2, "/bin/sleep", "/bin/sleep", "1");
}
Output:
child 31548 terminated on timeout with return code 15.
child 31549 terminated in time with return code 0.
With these changes your program produces the desired output:
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
using namespace std;
int main()
{
for (int round = 0; ++round <= 4; )
{
int i;
cout << "*** ROUND: " << round << " ***\n";
for (i=0; i<3; i++) // loop will run n times (n=3)
{
if (fork() == 0)
{
int pid = getpid();
cout << "Process p" << i+1 << " pid:" << pid << " started...\n";
unsigned int seconds = pid%10*3+5;
cout << "Process " << pid << " exiting after "
<< seconds-sleep(seconds) << " seconds\n";
exit(0);
}
}
while (i--) // loop will run n times (n=3)
{
int status;
cout << "Process " << wait(&status);
cout << " exited with status: " << status << endl;
}
}
}
As Serge suggested, we're using sleep() for every child before exiting it. it will pause the process for a number of seconds.
To get the actual status information, we call wait(&status) instead of wait(NULL).
We're doing this all for the first scheduling round plus the desired 3 times of rescheduling.

Running 3 child processes

I have three child process and one parent.
I want the program runs in that order [ Child1 then child2 then child3 then parent ].
I have been trying to do with the follwing code but it dose not give me right sequence!
Code:
#include<iostream>
#include<string.h>
#include<fstream>
#include<cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main()
{
pid_t ch11;
pid_t ch22;
pid_t ch33;
int ch1 = fork();
int ch2 = fork();
int ch3 = fork();
if (ch1==0) //child1
{ cout<<"this is child 1\nGoing from 1 to child 2 ...\n\n";
exit(0);
}
else if ( ch2==0)
{ waitpid(ch11,0,0);
cout<<"This is child 2\nGoing from 2 To Child 3 ...\n\n";
exit(0);
}
else if (ch3==0)
{ waitpid(ch22,0,0);
cout<<"This is child 3\nFineshed !! going from 3 to parent\n\n";
exit(0);
}
else
{ waitpid(ch33,0,0);
cout<<"This is parent , waited the whole childes to finish !!\n\n";
exit(0);
}
return 0 ;
}
Output:
ubuntu#ubuntu-desktop:~$ c++ a.cpp -o p1
ubuntu#ubuntu-desktop:~$ ./p1
This is child 2
Going from 2 To Child 3 ...
this is child 1
Going from 1 to child 2 ...
this is child 1
Going from 1 to child 2 ...
this is child 1
Going from 1 to child 2 ...
This is child 2
Going from 2 To Child 3 ...
This is child 3
Fineshed !! going from 3 to parent
this is child 1
Going from 1 to child 2 ...
This is parent , waited the whole childs to finish.
I know that it will be solved by using waitpid() function , I think im using the waitpid() wrong.
Here's a better instrumented version of your code; it includes the PID in the output, and the outputs are one line each.
#include <iostream>
#include <cstdlib>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
int ch1 = fork();
int ch2 = fork();
int ch3 = fork();
if (ch1 == 0) // child1
{
cout << (int)getpid() << ": This is child 1 - Finished\n";
exit(0);
}
else if (ch2 == 0)
{
waitpid(ch1, 0, 0);
cout << (int)getpid() << ": This is child 2 - Finished\n";
exit(0);
}
else if (ch3 == 0)
{
waitpid(ch2, 0, 0);
cout << (int)getpid() << ": This is child 3 - Finished!\n";
exit(0);
}
else
{
waitpid(ch3, 0, 0);
cout << (int)getpid() << ": This is parent - waited for all children to finish!\n";
exit(0);
}
return 0;
}
Sample output:
$ ./3kids
40287: This is child 3 - Finished!
40285: This is child 1 - Finished
40286: This is child 2 - Finished
40290: This is child 1 - Finished
40289: This is child 2 - Finished
40288: This is child 1 - Finished
40284: This is parent - waited for all children to finish!
40291: This is child 1 - Finished
$
As you can see, there is one process that considers itself to be child 3, two processes that consider themselves to be child 2, and four processes that consider themselves to be child 1 and one that considers itself to be the parent. This is consistent with the unconstrained forking which creates 8 processes.
To have 3 children only, and to wait for each in turn, you need code more like:
#include <iostream>
#include <cstdlib>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
void child(int n)
{
flush(cout); // No pending output
int pid = fork();
if (pid < 0)
cerr << (int)getpid() << ": failed to fork\n";
else if (pid == 0)
{
cout << (int)getpid() << ": This is child " << n << " - Finished\n";
exit(0);
}
else
{
int corpse;
int status;
while ((corpse = wait(&status)) != -1)
cout << (int)getpid() << ": PID " << corpse << " exited with status "
<< status << "\n";
}
}
int main()
{
child(1);
child(2);
child(3);
cout << (int)getpid() << ": This is parent - waited for all children to finish!\n";
return 0;
}
Sample output:
$ ./3kids
40336: This is child 1 - Finished
40335: PID 40336 exited with status 0
40337: This is child 2 - Finished
40335: PID 40337 exited with status 0
40338: This is child 3 - Finished
40335: PID 40338 exited with status 0
40335: This is parent - waited for all children to finish!
$
Despite your comment "the code is working but it gives me the wrong sequence of executing" the whole thing is still completely incorrect and it is obvious you did not understand what other people tried to say, hence I'll make an attempt here.
int main()
{
pid_t ch11;
pid_t ch22;
pid_t ch33;
What is the purpose of these variables?
int ch1 = fork();
Have you read fork manpage? It clearly states when a child process is created it returns 0 in the child and pid in the parent. It can also fail, so you should have checked for that.
With this in mind....
int ch2 = fork();
Both the child and the parent reach this line. Hence previously created child forks as well.
int ch3 = fork();
Guess what.
if (ch1==0) //child1
{ cout<<"this is child 1\nGoing from 1 to child 2 ...\n\n";
Except you are not (see below).
exit(0);
Should be _Exit.
}
else if ( ch2==0)
{ waitpid(ch11,0,0);
What? ch11 is not even initialized, so what was this supposed to accomplish?
Do you compile your code with warnings enabled?
In general I don't know if you were trying to make the children fork and thus create a chain of for processes with parent<->child relationship or you wanted 3 children of the same parent process. If the latter this is just bad, if the former this is even more wrong since you would not be able to just wait for such a process.
[snip the rest]
Finally, let's address
the wrong sequence of executing
What?
There are no guarantees whatsoever as to the order of execution of your processes. In fact any number of them can execute for any amount of time and be scheduled out multiple times before any other number of them even gets a chance to run.
Given what was shown up to this point I'm inclined to guess you either misunderstood your homework assignment or are approaching it quite "unconventionally". Either way, I strongly recommend you state the actual problem which was supposed to be addressed with your code.

Why am I having to terminate my program manually?

I have a program that should launch another process and work simultaneously with it. I am using fork() and system() to accomplish this. I have code verifying that that my system() call returns, but every time I execute I have to manually end the execution by typing ctrl c. I can tell that one of my processes terminates, but I am not quite sure which one. I believe it is the parent. Both should be caught by return statements though.
Here is the parent process code (forkWaitTest):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
int main(void)
{
pid_t childPID;
int status;
childPID = fork();
if(childPID >=0)
{
if(childPID == 0)
{
cout <<"I'm the fork child";
cout.flush();
status=system("child");
cout << "status = " << status;
//exit(0);
//cout << "Am I getting here?";
}
else{
cout << "I am the parent and can keep doing things ";
int y=1;
int x=7;
cout<< y+x << " ";
}
}
return 0;
}
This is the child process that is called
#include <stdio.h>
int main(int argc, char *argv[] )
{
printf("I am the child\n");
return 0;
}
Here is my output:
-bash-4.2$ forkWaitTest
I am the parent and can keep doing things 8 I'm the fork child-bash-4.2$ I am the child
status = 0
Your parent DID terminate. Let's look at your output:
-bash-4.2$ forkWaitTest
I am the parent and can keep doing things 8 I'm the fork child-bash-4.2$ I am the child
status = 0
Now let's look at that more closely, particularly this bit:
I'm the fork child-bash-4.2$
See, right in the middle, it says bash-4.2$. That's the command prompt after the parent terminates. Then the child runs and also exits, but you are confused as it prints after the bash prompt. Your ^C merely prints the next line.
To verify, press return a few times rather than ^C.
Make sure your messages end with newlines (add << endl to the outputs, for example).
I see your bash prompt (the -bash-4.2$) mixed up with the other outputs, partly because you don't have newlines in the outputs, and partly because your code doesn't show any attempt to wait for children to die. This means that your process did die.
You could type: ls -l and the shell would execute that command because it is waiting for you to type something. When you interrupt it (the shell), it prompts again, but it would have taken your input even without the interrupt.
With a slightly modified version of the child:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("I am the walrus (%d)\n", (int)getpid());
return 0;
}
and a slightly modified version of the parent:
#include <cstdlib>
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main(void)
{
pid_t childPID;
int status;
childPID = fork();
if (childPID >= 0)
{
if (childPID == 0)
{
cout << "I'm the forked child (" << getpid() << ")\n";
cout.flush();
status = system("child");
cout << "status = " << status << endl;
cout << "Am I getting here?\n";
return 0;
}
else
{
cout << "I am the parent and can keep doing things ";
int y = 1;
int x = 7;
cout << y + x << endl;
}
}
int corpse;
while ((corpse = wait(&status)) != -1)
cout << "PID " << corpse << " exited with status " << status << endl;
return 0;
}
and with a prompt Osiris JL:, an example of the output I get is:
Osiris JL: ./parent
I am the parent and can keep doing things 8
I'm the forked child (3192)
I am the walrus (3193)
status = 0
Am I getting here?
PID 3192 exited with status 0
Osiris JL:
Note that the 'child' program is run by a child of the parent process's own child (that's why the getpid() values are printed). And the wait() in a loop in the parent process ensures that the child has died before the parent dies. Given the structure of the child process, which uses the system() function, the grandchild process (running the partly misnamed child process) has exited before the child exits before the parent exits. Omit the wait() loop and the parent can easily exit before the child and grandchild do:
Osiris JL: ./parent
I am the parent and can keep doing things 8
I'm the forked child (3207)
Osiris JL: I am the walrus (3208)
status = 0
Am I getting here?
ls -ld parent* child*
-rwxr-xr-x 1 jleffler staff 8784 Sep 20 15:57 child
-rw-r--r-- 1 jleffler staff 122 Sep 20 15:57 child.cpp
drwxr-xr-x 3 jleffler staff 102 Sep 20 15:57 child.dSYM
-rwxr-xr-x 1 jleffler staff 9808 Sep 20 16:02 parent
-rw-r--r-- 1 jleffler staff 858 Sep 20 16:02 parent.cpp
drwxr-xr-x 3 jleffler staff 102 Sep 20 16:02 parent.dSYM
Osiris JL: