How to make background processes in C?

Hi,
I have a question about running background processes in a shell. I have to run dir in the background.
My thinking for this to work is to get rid of the call to waitpid in the parent clause. Am I correct here?

Any help is appreciated
Thanks in advance
if(strcmp (instruction, "dir") == 0){
	char* directory = (char*) malloc(strlen(command)-3);
	substr(directory, command, 4, strlen(command)); 

	char* a = (char*) malloc (strlen(directory) + 25);
	a[0] = '\0';
	strcpy(a, "/usr/bin/ls");
	
	char al[3];
	strcpy(al, "-al");
	
	int status;
	pid_t childPID = fork();
	if (childPID == -1){
		// fail clause
		printf("Failed to fork.");
	}
	else if (childPID == 0){
		//child clause
		argVector[0] = a;
		argVector[1] = al;
		argVector[2] = directory;
		argVector[3] = 0;
		
		execve(a, argVector, environ);
		exit(0);
	}
	else{
		// parent clause
		waitpid(childPID, &status, WUNTRACED);
	}
}

Open in new window

tdeepu100Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

evilrixSenior Software Engineer (Avast)Commented:
What are you trying to achieve? Yes removing the waitpid will stop the parent waiting on the child but if you do that how will yo know when it's done?
0
tdeepu100Author Commented:
I am trying to run the dir process in the background and then report the results once it gets done.
So when the dir process is running I should still be able to input different commands.
0
evilrixSenior Software Engineer (Avast)Commented:
You'd be better off creating a thread to do this and in that thread use opendir() to enumerate the directory and stat() to get the details you're interested in.
0
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

Hugh McCurdyCommented:
Report the results how/where?

If the report is to a printer, or anything that isn't the controlling console, then just removing the waitpid is enough because it's "fire and forget."

If however, you want the controlling program to process the results, this gets more complicated (thus my question).
0
tdeepu100Author Commented:
yea im actually going for a controlling program..
any examples about that would be great!
0
Hugh McCurdyCommented:
I would try eviltrix's thread suggestion.  Here's a resource that might help.

http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
0
HappyCactusCommented:
You should in any case call waitpid somewhere in the code, since if you do not do this you'll result with a zombie processes. a Zombie process is a child process that ends without the parent have collected the lost resources. So even if you "fire and forget" that process, you'll have to call waitpid when done.
You can achieve this by handling SIGCHLD and call wait or waitpid. See this (first listing):

http://www.steve.org.uk/Reference/Unix/faq_8.html

 
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Hugh McCurdyCommented:
HappyCactus - I knew there was a way and I couldn't remember it.  Been so long.  Good post.

tdeepu100 - You could open a pipe (which can be done with popen/pclose).  Then collect data from the child program.  

One problem with this idea is that you'll end up with blocking i/o by default.  (So, you might want to use pipe and read.  I think you can do non-blocking i/o on a pipe.  I think I did; but it was 15 years ago and I don't remember the details.)

Anyway, if you use non blocking reads, you can wait for your interrupt (SIGCHLD) and then test each of the pipes (if more than one) for data.

Still, this whole mess can potentially get confusing and if not done exactly right can lead to the equivalent of a deadlock.  Then the program will stop working.  (Of course, you could set an alarm or have the user press ^C, to get SIGINT, but this is all kludgy).

The thread idea is still likely the best idea.  (Just that I was doing this kind of work before the thread support existed and haven't done much of this type of work since.)
0
ecwCommented:
Do not use a thread instead of fork, you're calling execve.  An exec in a thread is the same as an exec anywhere else in a programme.  The calling process is "replaced" by a new programme.

** quick aside, also do not call exit after a failed exec, use _exit.

Yes you are right, waitpid is the culprit.  Call it with WNOHANG, but not where you are.  If you fire off a a foreground process, before you print the next prompt, call waitpid with the forground pid and without WNOHANG.  Then, of if you only have background processes, use pid of -1 and WNOHANG.  Something like this, but bear in mind, I haven't access to one of my unix boxes at the mo -

if (fgpid != (pid_t) -1) {
  /* foreground process - all we care about for now */
  pid = waitpid(fgpid, &status, WUNTRACED);
  /* now examine status to determine what to do next, eg. has it stopped or died or ... */
}
/* now deal with any problem background children */
while ((pid = waitpid((pid_t) -1, &status, WUNTRACED | WNOHANG)) != (pid_t) -1) {
  /* again  examine status to determine what to do with the child */
}
/* if this point was reached, we know this "shell" needs to get back to foreground, so */
/* do the suaul stuff, tcsetpgrp etc. and print prompt, read next command, and repeat ... */

Of course ymmv, but that's the general gyst of things.  More can be done if you're prepared to trap SIGCHLD (see sigaction - SA_RESTART flag's useful here).  But waitpid pretty much does away with a lot of the reasons for trapping SIGCHLD.
0
evilrixSenior Software Engineer (Avast)Commented:
>> Do not use a thread instead of fork, you're calling execve.
I'm not sure I follow your thought process here. Maybe you could elaborate what that has to do with solving this issue using threads rather than processes?
0
HappyCactusCommented:
exec*() substitute the entire process with a new process.
So if you are calling exec*() within a thread, your thread, and all the other thread, will terminate.
So inside your thread you should call fork() and then exec.
But between fork() and exec() there are two copies of each same process - with the same thread working. You must ensure that all other thread in the forked(), child process, do not interfere with the original process. Race condition is a great risk in this case.

Hope I am been clear, if not do not hesitate  to ask.
0
stefan73Commented:
HappyCactus:
Wouldn't that mean that the system() call is not thread-safe? That's news to me.

I agree, though, that the time between fork() and exec() should be as short as possible (e.g., create the arguments and all needed data before forking).
0
evilrixSenior Software Engineer (Avast)Commented:
HappyCactus,

I think my original post has been misunderstood. I never suggested running fork/exec in a thread (what would be the point of that?).. I suggested spawning a thread and generating the required stats using opendir()/stat.

"You'd be better off creating a thread to do this and in that thread use opendir() to enumerate the directory and stat() to get the details you're interested in."

Or, to put it another way spawning a new process to execute a system command is a very poor way to do what the asker is trying to do both in terms of efficiency, cohesiveness and overall control of the result.

>> Wouldn't that mean that the system() call is not thread-safe? That's news to me.
Since C/C++ have no concept of threads, unless your C Runtime (CRT) has been built specifically for thread safety we can assume that no standard function is thread safe. For example, most of them modify the errno global variable. In a thread safe CRT this will be set using Thread Local Storage (TLS) but this is not defined by the standard.
0
HappyCactusCommented:
Yes. But I have not found reference about it.
(it is difficult to google the right thing with the "system" word)

No, "as short as possible" is not acceptable. It is not thread-safe. They *must* be atomic.
This is a good article about fork() and threads. the author literally says:

"In my opinion there are so many problems with fork(2) in multi-threaded programs that it's almost impossible to do it right. The only clear case is to call execve(2) in the child process just after fork(2). If you want to do something more, just do it some other way, really. From my experience it's not worth trying to make the fork(2) call safe"

http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them

0
HappyCactusCommented:
evilrix,

I agree with your solution! It's _the_ right solution, that should be implemented like you suggested.
I do not like call shells from inside a program - it is dangerous for various things and it must be done correctly - a bad path can subvert your program.

But I wanted to clarify why he should not call execve or fork() inside a thread.
0
evilrixSenior Software Engineer (Avast)Commented:
>> But I wanted to clarify why he should not call execve or fork() inside a thread.
Understood. Thanks :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.