?
Solved

forking in C++

Posted on 2003-02-18
6
Medium Priority
?
643 Views
Last Modified: 2012-06-21
I would like to have some tutorials for learning forking in C++
I was trying to search in web but in vain.
kindly help me out.  This is more urgent.
0
Comment
Question by:Renukak
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
6 Comments
 
LVL 11

Expert Comment

by:cup
ID: 7980057
Which operating system?
0
 

Expert Comment

by:rde011176
ID: 7980319
Are u trying in Windows? You may look at CreateThread or CreateProcess Win32 API's
0
 
LVL 12

Accepted Solution

by:
Salte earned 120 total points
ID: 7980514
The unix fork function is available in C++ just like in C:

void do_something()
{
   pid_t pid;

   switch (pid = fork()) {
   case 0:
       // I am child...
       ....
       break;
   case -1: // error, fork failed.
      ....
      break;
   default:
      // I am parent, pid is pid of child.
      .....
      break;
   }
}

YOu can have a set of classes etc but it is really irrelevant as far as fork() goes. C and C++ are identical when it comes to fork().

Now, you might want to have a set of class objects where one object represent the parent and another represent the child. If so you can pack the fork() function inside a class:

class Process {
private:
   pid_t pid_;
   Process * parent_;
   ...other data...

   static Process * self_;

   Process(pid_t p, Process * par) : pid_(p), parent_(par)
   {}

public:
   Process * fork();
   Process * parent() { return parent_; }
   pid_t pid() const { return pid_; }
   static void init(); // to initialize.
   static Process * self() { return self_; }
};

Here you want fork to throw an exception if it goes wrong and return a 0 pointer to child - the child can still get its object by calling the static::self() function - and return a non-null pointer to parent representing the child process:

Process * Process::fork()
{
   pid_t pid;
   Process * p;

   switch (pid = ::fork()) {
   case 0: // I am child.
      pid = getpid(); // my pid.
      // current self_ is my parent.
      p = new Process(pid,self_);
      self_ = p;
      return 0;
   case -1:
      throw some_exception("fork");
   default: // I am parent, pid is child.
      // self_ is me - the new process' parent.
      p = new Process(pid,self_);
      return p;
   }
}

Now if you initialize the class by the following function.
This function is only called early in the main process, the children doesn't call init.

void Process::init()
{
   pid_t pid = getpid(); // get my own pid.
   // If I don't have a process object for my own
   // parent I can use a 0 pointer here.
   // if I want an object for my parent I can call
   // getppid() to get my parent's pid and initialize
   // an object with that pid and then use a pointer
   // to that object as the parent object for this
   // object.
   Process * p = new Process(pid,0);
   self_ = p;
}

Now each process can call Process::self() to get the object describing themselves and self() -> parent() will give the object describing their parent or 0 if the parent isn't represented. self() -> pid() gives their own pid.

If you want the children represented in a list you can do that too:

step 1. Include a std::list<Process *> children; element in the Process class.

step 2. Let fork add the new process to the list of the parent. This must be done in both the child code and the parent code. Probably best done by having each of them do it separately for their structures (after the fork the child and the parent have their own image of the structure).

The above structure has two flaws:

1. At the moment of the fork() the child gets a copy of the parent's data structure and so it knows what previous siblings etc has been created. However, later siblings, forks() done after itself - will not be reflected. The problem is that the data structure isn't shared among the processes.

2. At some point a process may want to exec(). It will destroy its own data structure over these things but the parent will still have the structure - however, it might want to know that you are about to exec and thereby update its process info about the process that want to exec().

3. At some point a process want to exit(). This should cause the Process() object to be removed from the data structures or possibly kept with a status of 'exited' and with a status code or exit code attached to it. The above framework has no support for that.

All of these things point towards a shared data structure where you have only one copy of each process object and that each process share the data structure that describes these processes.

One way to achieve this is to use memory mapping or shared segments. Both things are platform specific and I won't go further into details about this. However, it also require that every time you want to update such info you must synchronize yourself with others, i.e. you must use semaphores and so on. Possibly the fork() function is such that only the parent actually create the Process() objects but the child must none-the-less wait for the Process() object to have been created before it can return from Process::fork(). Also, the previously static variable must now be made outside of this data structure - i.e. it is NOT shared among the processes. This is largely autmoatic since it is static it isn't part of the structure data that is allocated on heap anyway. So, all you have to make sure is that the Process object when allocated from heap is allocated from that shared heap. Since that heap contain only Process objects it is probably best to look at the shared segment as an array of Process objects and each new gets one element and free mark it as free and sticks it back in a free list. Overriding Process::new will solve this problem:

void * Process::new(size_t sz)
{
   // first wait for mutex or semaphore, Process objects
   // are allocated from shared segment so all operations
   // must be guarded.
   Process * p = free;

   wait_for_mutex();
   if (p != 0) {
      // when a process object is free, the parent_ is
      // next object in free list.
      free = p -> parent_;
   } else if ((p = last_process) < end_area) {
      // we still have free unused slots at the end.
      ++last_process;
   } else {
      // if we can expand area we can try to do so
      // otherwise we give up at this point.
      p = 0;
   }
   release_mutex();
   return static_cast<void *>(p);
}

similarly:

void Process::delete(void * ptr)
{
   Process * p = reinterpret_cast<Process *>(ptr);
   wait_for_mutex();
   p -> parent_ = free;
   free = p;
   release_mutex();
}

Now the variables 'free', 'last_process' and 'end_area' deserve special attention. They are not per process so they are static just like self_. However, unlike self_ they are shared so they should reside in that same area space where the heap itself comes from. It is probably therefore best to not use pointers directly but rather pointers to pointers and initialize those pointers to point to where the real pointers are when the heap is initialized. The heap is of course initialized in Process::init().

Also, to update the data structure when a process do exec() or exit() or another fork() one must essentially do the following:

If the program is co-operating, i.e. it knows that we want to keep up-to-date information about the process and it can tell us whenever its state change this is easy enough:

It never do fork() or exec() instead it uses Process::fork() and Process::exec(). exit() is a little harder but that is also easily amended, just define a static variable with constructor/destructor in the class or provide an at_exit() function that is called when a process is about to die. In this case the process call that function and that function can call a member function in Process to update the state.

If the program isn't or cannot be trusted to be co-operating you have to do something trickier. This is very platform dependent but essentially you intercept function calls like fork(), exec() and exit() and any other similar calls that might change the status of the process and then update the data structure when you see the process tries to do those calls. In a unix system using shared libraries (.so) this is relatively easy to do by just providing your own .so file with those functions defined. You then must do some trickery when you want to forward the fork() etc to do the actual fork since just calling fork() will simply imply a recursive call to yourself. However, this is relatively easy to solve and can be done by calling the function in a specific other .so file, libc.so* is a good bet for a file defining fork() as a function to call to avoid the recursive call under a Unix or Linux type system. Similar things can be done under windows manipulating LoadLibrary() etc.

Thus you can supervice processes if you want.

Now if this is not what you meant by 'fork() in C++' I really don't know what you mean because any other way of fork has nothing to do with C++ and is the same for C and C++ and any other language where you can call fork().

Alf
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 2

Expert Comment

by:udil
ID: 8867131
This question has been abandoned. I will make a recommendation to the moderators on its resolution in a week or two. I appreciate any comments that would help me to make a recommendation.

In the absence of responses, I may recommend DELETE unless it is clear to me that it has value as a PAQ. Silence = you don't care

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Udil
EE Cleanup Volunteer
0
 
LVL 12

Expert Comment

by:Salte
ID: 8867431
Considering that renukak specified this question to be "urgent" he appearanly didn't feel the same urgency to accept any answer.... He didn't even feel any urgency to respond to any answer or lack thereof. Oh well, that is his problem I think.

I do believe I did give some info about forking and classes there that's worth keeping though.

Alf
0
 
LVL 2

Expert Comment

by:udil
ID: 8950577
This question didn't show any activity for more than 21 days. I will ask Community Support to close it unless you finalize it yourself within 7 days.
Unless there is objection or further activity,  I will suggest to

    Answered by: Salte"

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Udil
EE Cleanup Volunteer
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
Suggested Courses

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question