C function to start process but to return right away

Hello,

I am using C on a Centos box with gcc as the compiler.

I want a function to do something, them make an http request to some server (most probably using curl but suggestions are welcome) and return right away without waiting for the server's answer on that request.

What should I use (library, function, design...) to achieve that?

Thank you

fork and exec. fork() copies your process but returns a different value to the copy so it can take a new direction. exec() replaces it with a different program. the original program can do other things and/or wait for the child to complete. see man fork, man execv, man wait.

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
  pid_t pid=fork();
  if(pid < 0)
  {
    perror("Couldn't fork");
    exit(1);
  }
  else if(pid == 0) // child code
  {
    execv("/bin/echo", "/bin/echo", "asdf", NULL);
    // this should never happen since execv REPLACES the process that calls it
    exit(1);
  }
  else // parent code
  {
    int status;
    // do other stuff.  child will keep running.
    sleep(5);

    // wait for child to complete
    wait(&status);
    printf("child returned %d\n", WEXITSTATUS(status));
    return(0);
  }
}
1 Like

Thanks! I like the strategy. I am putting those codes inside a fastcgi program:

int main(void)
{
  initialize();
  while (FCGI_Accept() >= 0)  
  {
		  pid_t pid = fork();
		  if(pid < 0)
		  {
		    exit(1);
		  }
		  else if(pid == 0)
		  {
		    //do things
				  continue;
		  }
		  bstring s = bfromcstr("Content-type: text/html\n\n");

    bcatcstr(s, "hellow");
    printf("%s",s->data);
  }
  return EXIT_SUCCESS;
}

From initial testing, it works fine. Do you see any pitfalls though?

Thanks agains

You're not calling wait() anywhere, so you may be generating lots of zombie processes -- processes that have completed but haven't been waited for.

You may wish to keep a thread around that does nothing but call wait() in a loop to handle them. You could also catch the SIGCHLD signal but this is subject to problems.[COLOR="\#738fbf"]

int main(void)
{
  initialize();
  while (FCGI_Accept() >= 0)  
  {
		  pid_t pid = fork();
		  if(pid < 0)
		  {
		    exit(1);
		  }
		  else if(pid == 0)
		  {
		    //do things
				  continue;
		  }
		  bstring s = bfromcstr("Content-type: text/html\n\n");

    bcatcstr(s, "hellow");
    printf("%s",s->data);
  }
  return EXIT_SUCCESS;
}

You are not making your child processes quit! That continue should be an exit, unless you want growing swarms of copies of your process all waiting on while (FCGI_Accept() >= 0).

1 Like

You'r right. 100% right. I should call exit(1) instead of continue. At that point, right after this exit, isn't the child process killed? If the child process is killed why would I need a way to handle zombies?
I don't want to wait for the child to return because I want to have the parent return as soon as possible.

The process terminates, but isn't removed from the process table until you wait() for it. Things like runtime statistics and return value can still be gleaned from it until you do, which is why they keep it. You don't need to wait() for it at the bottom of your loop there(and thereby block until it quits) but something in your program must do it sooner or later.

Fortunately it's not a big deal. There's nonblocking ways to check if a child has exited ( see waitpid's WNOHANG flag ), ways to handle child exits asynchronously with sigaction and SIGCHLD(not reccomended; if you get two SIGCHLD's too fast to handle you might miss one), or you can just do a wait() loop in an independent thread. The thread method's probably the most bulletproof and least resource-intensive, the thread will spend 99.9% of its time asleep and only run when needed. (or when you want it to.)

I think you misunderstood a little with the exit(1). It doesn't have to be exit(1). In the example I showed I used execv(), which replaces the process with something else, so the exit(1) would only happen if execv() failed to load /bin/echo. If you're just doing your own thing you could have an exit(0) for normal return and exit(1) on error and so forth, it's just the return value for main().

---------- Post updated at 03:15 PM ---------- Previous update was at 02:58 PM ----------

It occurs to me to ask, how many loops does this program run? I figured it was like a daemon that takes several requests, which could create zombies forever which would be bad. If the loop only runs once and it quits thereafter, it's probably safe not to wait() if the child is disowned first. It will be owned by init instead and waited for by it. Researching how to do this.

1 Like

It is a fastcgi program that interacts with a nginx webserver through a socket. I am using supervisord to spawn instances of this progam.
The number of loops could be infinite.

If the number of loops, hence the number of children, could be infinite, you really should call wait() for all its children. But you don't need to block in order to do so, just create a thread for it in the background.

Since this process is persistent, you could also create threads for this, which may be more efficient. Forking processes like this allows them to continue when the parent quits, while threads would not. Threads are also subject to race conditions(seperate contexts all in the same process sharing most of the same memory).

1 Like

Hello,
My mistake of using continue and not taking care of those children properly has taken supervisord down as supervisord could not access the socket, which was already used by a program (probably one of those zombies). I killed all the instanced of the app and supervisord restarted properly.
I must take care of that issue!
Let me spend some time on understanding how threads would work here.

---------- Post updated 06-09-10 at 04:09 PM ---------- Previous update was 06-08-10 at 09:31 PM ----------

Hi,
I was able to give another looks at the supervisor's doc (the site was down for a few hours) and it appears that

This explains why I had the problems above. Now, I guess my best strategy is to use threads.