?
Solved

forking in C++

Posted on 2003-02-18
6
Medium Priority
?
649 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
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
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

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!

Question has a verified solution.

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

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
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…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

571 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