I am trying to create multiple processes from a same parent, but it always ended up with more processes than expected. I couldn't figure out how to do it and need some help here.
I found a piece of code online and tried it,
int main ()
{
pid_t pid=0;
int i=0;
for (i=0; i<3; i++)
{
pid=fork();
switch(pid)
{
case 0:
{
cout<<"\nI am a child and my pid is:"<<getpid();
cout<<endl;
exit(0);
break;
}
default:
{
cout<<"\nI am a parent and my pid is: "<<getpid();
cout<<"\nMy child pid is: "<<pid;
cout<<endl;
wait(NULL);
break;
}
}
}
return 0;
}
This code does work and creates 3 children from same parent. However, it seems like that's because after each child process was created, it was terminated immediately. So it won't fork more grandchild process in the next round of for loop. But I need to keep these child processes running for sometime and they need to communicate with the parents.
A child process may immediately break the loop to continue its work outside
int main ()
{
cout<<"\nI am a parent and my pid is: "<<getpid()<<endl;
pid_t pid;
int i;
for (i=0; i<3; i++)
{
pid=fork();
if(pid == -1)
{
cout<<"Error in fork()"<<endl;
return 1;
}
if(pid == 0)
break;
cout<<"My child "<<i<<" pid is: "<<pid<<endl;
}
if(pid == 0)
{
cout<<"I am a child "<<i<<" and my pid is "<<getpid()<<endl;
wait(NULL); // EDIT: this line is wrong!
}
else
{
cout<<"I am a parent :)"<<endl;
wait(NULL); // EDIT: this line is wrong!
}
return 0;
}
EDIT
The wait(NULL) lines are wrong. If the process has no children active, wait() has no effect, so it's useless in children here. OTOH in the parent process wait() suspends the execution until any of children exits. We have three children here, so would have to wait() three times. Additionally one can't know in advance the order of children completion, so we would need much more sophisticated code for that. Something like this:
struct WORK_DESCRIPTION {
int childpid;
// any other data - what a child has to do
} work[3];
for(i=1; i<3; i++) {
pid=fork();
...
work[i].childpid = pid;
}
if(pid == 0) // in a child
{
do_something( work[i] );
}
else
{
int childpid;
while(childpid = wait(NULL), childpid != 0)
{
// a child terminated - find out which one it was
for(i=0; i<3; i++)
if(work[i].childpid == childpid)
{
// use the i-th child results here
}
}
// wait returned 0 - no more children to wait for
}
Related
In Linux, one can wait on any FD using select, poll or epoll.
It is also possible to wait for child-processes to change state using wait, waitpid or waitid.
However, I can't figure a way to combine these operations, i.e., to block the calling process until either some FD becomes ready or a child process changes state.
I can use polling, by repeatedly calling non-blocking epoll then waitid, but that is wasteful.
It is possible to create a pidfd for a child process (which is accepted by epoll), but pidfd only supports waiting for child termination, while I wish to wait for any state change (specifically, for ptrace stops).
Is this not possible in Linux?
You can wait for any child status change with signalfd() and make dummy read, then get actual status with waitpid():
sigset_t mask, old_set;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &old_set);
int sigfd = signalfd(-1, &mask, SFD_CLOEXEC);
if (sigfd == -1) {
perror("signalfd");
return 1;
}
for (int i = 0; i < 10; ++i) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
}
if (pid == 0) {
// Child process: restore blocked signals before exec() etc
sigprocmask(SIG_SETMASK, &old_set, NULL);
sleep(i % 3);
switch (i % 3) {
case 0:
raise(SIGSTOP);
break;
case 1:
raise(SIGABRT);
break;
}
exit(i);
}
printf("Spawned child %i with pid %u\n", i, pid);
}
for (;;) {
struct pollfd fds[] = {
{ .fd = STDIN_FILENO, .events = POLL_IN },
{ .fd = sigfd, .events = POLL_IN }
};
if (poll(fds, sizeof(fds)/sizeof(*fds), -1) == -1) {
perror("poll");
break;
}
if (fds[0].revents & POLL_IN) {
char buf[4096];
int ret = read(STDIN_FILENO, buf, sizeof(buf));
printf("Data from stdin: ");
fflush(stdout);
write(STDOUT_FILENO, buf, ret);
}
if (fds[1].revents & POLL_IN)
{
struct signalfd_siginfo fdsi;
read(sigfd, &fdsi, sizeof(fdsi));
for (;;) {
int status;
pid_t pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);
if (pid == -1) {
if (errno != ECHILD) {
perror("waitpid");
}
break;
}
if (pid == 0) {
break;
}
printf("Child %u ", pid);
if (WIFEXITED(status)) {
printf("exited with status %i\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("terminated by signal %i\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %i\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
} else {
printf("status unknown\n");
}
}
}
}
close(sigfd);
I am porting a debugger, 'pi' ('process inspector') to Linux and am
working on the code for fork/exec of a child to inspect it. I am
following standard procedure (I believe) but the wait is hanging.
'hang' is the procedure which does the work, the 'cmd' argument being
the name of the binary (a.out) to trace:
int Hostfunc::hang(char *cmd){
char *argv[10], *cp;
int i;
Localproc *p;
struct exec exec;
struct rlimit rlim;
i = strlen(cmd);
if (++i > sizeof(procbuffer)) {
i = sizeof(procbuffer) - 1;
procbuffer[i] = 0;
}
bcopy(cmd, procbuffer, i);
argv[0] = cp = procbuffer;
for(i = 1;;) {
while(*cp && *cp != ' ')
cp++;
if (!*cp) {
argv[i] = 0;
break;
} else {
*cp++ = 0;
while (*cp == ' ')
cp++;
if (*cp)
argv[i++] = cp;
}
}
hangpid = fork();
if (!hangpid){
int fd, nfiles = 20;
if(getrlimit(RLIMIT_NOFILE, &rlim))
nfiles = rlim.rlim_cur;
for( fd = 0; fd < nfiles; ++fd )
close(fd);
open("/dev/null", 2);
dup2(0, 1);
dup2(0, 2);
setpgid(0, 0);
ptrace(PTRACE_TRACEME, 0, 0, 0);
execvp(argv[0], argv);
exit(0);
}
if (hangpid < 0)
return 0;
p = new Localproc;
if (!p) {
kill(9, hangpid);
return 0;
}
p->sigmsk = sigmaskinit();
p->pid = hangpid;
if (!procwait(p, 0)) {
delete p;
return 0;
}
if (p->state.state == UNIX_BREAKED)
p->state.state = UNIX_HALTED;
p->opencnt = 0;
p->next = phead;
phead = p;
return hangpid;
}
I put the 'abort()' in to catch a non-zero return from ptrace,
but that is not happening. The call to 'raise' seems to be a
common practice but a cursory look at gdb's code reveals it is
not used there. In any case it makes no difference to the outcome.
`procwait' is as follows:
int Hostfunc::procwait(Localproc *p, int flag){
int tstat;
int cursig;
again:
if (p->pid != waitpid(p->pid, &tstat, (flag&WAIT_POLL)? WNOHANG: 0))
return 0;
if (flag & WAIT_DISCARD)
return 1;
if (WIFSTOPPED(tstat)) {
cursig = WSTOPSIG(tstat);
if (cursig == SIGSTOP)
p->state.state = UNIX_HALTED;
else if (cursig == SIGTRAP)
p->state.state = UNIX_BREAKED;
else {
if (p->state.state == UNIX_ACTIVE &&
!(p->sigmsk&bit(cursig))) {
ptrace(PTRACE_CONT, p->pid, 1, cursig, 0);
goto again;
}
else {
p->state.state = UNIX_PENDING;
p->state.code = cursig;
}
}
} else {
p->state.state = UNIX_ERRORED;
p->state.code = WEXITSTATUS(tstat) & 0xFFFF;
}
return 1;
}
The 'waitpid' in 'procwait' just hangs. If I run the program with
the above code, and run a 'ps', I can see that 'pi' has forked
but hasn't yet called exec, because the command line is still
'pi', and not the name of the binary I am forking. I discovered
that if I remove the 'raise', 'pi' still hangs but 'ps' now
shows that the forked program has the name of the binary being
examined, which suggests it has performed the exec.
So, as far as I can see, I am following documented procedures to
take control of a forked process but it isn't working.
Noel Hunt
I have found the problem (with my own code, as Nate pointed out), but the cause was obscure until I ran 'strace pi'. It was clear from that that there was a SIGCHLD handler, and it was executing a wait. The parent enters wait, SIGCHLD is delivered, the handler waits and thus reaping the status of the child, then wait is restarted in the parent and hangs because there is no longer any change of state. The SIGCHLD handler makes sense because the pi wants to be informed of state changes in the child. The first version of 'pi' I got working was a Solaris version, and it uses /proc for process control so there was no use of 'wait' to get child status, hence I didn't see this problem in the Solaris version.
I have an assignment to implement Producer consumer problem in a convoluted way(may be to test my understanding). The parent process should set up a shared memory. The unnamed semaphores(for empty count and filled count) should be initialized and a mutex should be initialized. Then two child processes are created, a producer child and a consumer child. Each child process should create a new thread which should do the job.
PS: I have read that the semaphore's should be kept in a shared memory as they would be shared by different processes.
Please provide some hints, or suggest changes.
So far, I have done this:
struct shmarea
{
unsigned short int read;
unsigned short int max_size;
char scratch[3][50];
unsigned short int write;
sem_t sem1;// Empty slot semaphore
sem_t sem2;// Filled slot Semaphore
};
void *thread_read(void* args);
void *thread_write(void *args);
pthread_mutex_t work_mutex;
struct shmarea *shma;
int main()
{
int fork_value,i=0,shmid;
printf("Parent process id is %d\n\n",getpid());
int res1,res2;
key_t key;
char *path = "/tmp";
int id = 'S';
key = ftok(path, id);
shmid = shmget(key,getpagesize(),IPC_CREAT|0666);
printf("Parent:Shared Memory id = %d\n",id);
shma = shmat(shmid,0,0);
shma->read = 0;
shma->max_size = 3;
shma->write = 0;
pthread_t a_thread;
pthread_t b_thread;
void *thread_result1,*thread_result2;
res1 = sem_init(&(shma->sem1),1,3);//Initializing empty slot sempahore
res2 = sem_init(&(shma->sem2),1,0);//Initializing filled slot sempahore
res1 = pthread_mutex_init(&work_mutex,NULL);
while(i<2)
{
fork_value = fork();
if(fork_value > 0)
{
i++;
}
if(fork_value == 0)
{
if(i==0)
{
printf("***0***\n");
//sem_t sem1temp = shma->sem1;
char ch;int res;
res= pthread_create(&a_thread,NULL,thread_write,NULL);
}
if(i==1)
{
printf("***1***\n");
//sem_t sem2temp = shma->sem2;
int res;
char ch;
res= pthread_create(&b_thread,NULL,thread_read,NULL);
}
}
}
int wait_V,status;
res1 = pthread_join(a_thread,&thread_result1);
res2 = pthread_join(b_thread,&thread_result2);
}
void *thread_read(void *args)
{
while(1)
{
sem_wait(&(shma->sem2));
pthread_mutex_lock(&work_mutex);
printf("The buf read from consumer:%s\n",shma->scratch[shma->read]);
shma->read = (shma->read+1)%shma->max_size;
pthread_mutex_unlock(&work_mutex);
sem_post(&(shma->sem1));
}
}
void *thread_write(void *args)
{
char buf[50];
while(1)
{
sem_wait(&(shma->sem1));
pthread_mutex_lock(&work_mutex);
read(STDIN_FILENO,buf,sizeof(buf));
strcpy(shma->scratch[shma->write],buf);
shma->write = (shma->write+1)%shma->max_size;
pthread_mutex_unlock(&work_mutex);
sem_post(&(shma->sem2));
}
}
(1) Your biggest problem by far is that you have managed to write a fork bomb. Because you don't exit either child in the fork loop each child is going to fall through and loop around and create their own children until you crash or bring the system down. You want something more like this:
while(i < 2)
{
fork_value = fork();
if(fork_value > 0)
i++;
if(fork_value == 0)
{
if(i==0)
{
printf("0 child is pid %d\n", getpid());
int res;
res = pthread_create(&a_thread,NULL,thread_write,NULL);
res = pthread_join(a_thread,&thread_result1);
exit(0);
}
if(i==1)
{
printf("1 child is pid %d\n", getpid());
int res;
res = pthread_create(&b_thread,NULL,thread_read,NULL);
res = pthread_join(b_thread,&thread_result2);
exit(0);
}
}
}
for (i = 0; i < 2; ++i)
wait(NULL);
Notice the wait on the children which you neglected.
(2) Always check your return codes. They are like safety belts, a bit of a drag but so helpful when you crash. (Yes, I didn't take my advice here but you should.)
(3) These names are awful.
unsigned short int read;
unsigned short int write;
Stay away from naming variables after system calls. It's confusing and just asking for trouble.
(4) Terminology wise, processes with a common ancestor, like these, are related. The parent can open shared memory and other resources and pass it on to the children. Unrelated processes would, for example, multiple instances of program launched from different terminals. They can share resources but not in the "inherited" way forked processes do.
It's late and didn't get around to looking at what you are doing with the threads and such but this should get you started.
I want child processes run concurrently. Is it OK to write as to achieve this:
for(p = 0; p < N; p++){ //there will be N child processes
pidOfChild = fork();
if(pidOfChild == -1){
printf("fork() failed.\n");
exit(1);
}
else if(pidOfChild == 0){
//do sth
printf("I am a child and my PID is %d.\n", getpid());
exit(0);
}
}
It is okay, and if your parent process exits, you don't need to wait.
I try write program, which realize next idea:
After start, program using fork() and:
parent process stopped on function wait() (for waiting death child process);
child process use prctl(PR_SET_PDEATHSIG, SIGHUP), and setup signal handler (It's helps detect parent death);
After death any process, program use fork() again.
void forking_process() {
pid_t id;
printf("forking_process is called!\n");
if (id = fork()) {
parent_process_operation();
} else {
child_process_operation();
}
}
void parent_process_operation() {
int status = 0;
printf("It's parent process! Pid = %d\n", (int)getpid());
pid_t chid = wait(&status);
printf("Terminated child process with PID = %d\n", chid);
inform_about_parent_death(status);
}
void child_process_operation() {
printf("It's child process! pid = %d, ppid = %d\n",
(int)getpid(), (int)getppid());
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = inform_about_parent_death;
if (sigaction(SIGHUP, &sa, NULL))
fprintf(stderr, "sigaction error\n");
prctl(PR_SET_PDEATHSIG, SIGHUP);
while(1) {
printf("."); fflush(stdout);
sleep(1);
}
}
void inform_about_parent_death(int i) {
printf("Process is dead. Restart!\n");
forking_process();
}
int main (void) {
forking_process();
return EXIT_SUCCESS;
}
If I run this application, and in another terminal kill child process - then will create child process.
If I kill the parent process once, - signal handler started and call fork().
If I again kill the parent process, - signal handler not responded.
That is - prctl() in first process work, but prctl() in second child process don't work.
Why it is happen? How I can correct it's program?