Reputation: 27
I want to make a process tree like the picture below. I wrote the code below but if you look at the PIDs, you'll find there's a problem!
Process tree if you can't view the image:
//A-B (A parent of B)
//A-C
//B-D
//D-E
//D-F
My code:
void child(int index, int level, pid_t parent) { // this is going to be called recursively by all child processes
pid_t cpid[3];
int i = 0;
pid_t Lparent = getpid();
if (index == 0 && level == 1) { // this is B
if ((cpid[i] = fork()) == 0) { // D is going to execute this
child(i, 2, Lparent);
}
sleep(3);
for (i = 0; i < 1; i++) { // B will kill D
kill(cpid[i], SIGKILL);
}
printf("My PID: %d, My Parent's PID:%d\n", getpid(), parent); // this is B
sleep(7);
} else if (index == 0 && level == 2) { // this is D
for (i = 0; i < 2; i++) {
if ((cpid[i] = fork()) == 0) {
child(i, 3, Lparent);
}
}
sleep(5);
for (i = 0; i < 2; i++) {
kill(cpid[i], SIGKILL);
printf("My PID: %d, My parent's PID:%d\n", getpid(), parent);
sleep(5);
}
} else {
printf("My PID: %d, My Parent's PID:%d\n", getpid(), parent);
sleep(10);
}
}
int main() {
pid_t cpid[2];
pid_t root = getpid();
int i = 0;
for (i = 0; i < 2; i++) {
if ((cpid[i] = fork()) == 0) {
child(i, 1, root);
}
}
sleep(8);
for (i = 0; i < 2; i++) {
kill(cpid[i], SIGKILL);
}
printf("My Pid: %d\n", getpid());
return 0;
}
My output:
My PID: 211, My Parent's PID:206
My PID: 213, My Parent's PID:212
My PID: 214, My Parent's PID:212
My PID: 210, My Parent's PID:206
My Pid: 206
Does anyone know how I can fix this?
Upvotes: 1
Views: 3388
Reputation: 753695
Here's what I came up with. It can easily be adapted to any other process tree structure. With a bit of care, you could have different functions doing different 'childish' things.
/* SO 7393-6920 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "stderr.h"
struct SubProcess
{
const char *name;
size_t nkids;
const char **kids;
};
struct SubProcess pstree[] =
{
{ .name = "A", .nkids = 2, .kids = (const char *[]){ "B", "C" } },
{ .name = "B", .nkids = 1, .kids = (const char *[]){ "D" } },
{ .name = "C", .nkids = 0, .kids = NULL },
{ .name = "D", .nkids = 2, .kids = (const char *[]){ "E", "F" } },
{ .name = "E", .nkids = 0, .kids = NULL },
{ .name = "F", .nkids = 0, .kids = NULL },
};
enum { NUM_PSTREE = sizeof(pstree) / sizeof(pstree[0]) };
static void be_childish(const struct SubProcess *subproc);
static void wait_for_kids(const char *name)
{
int corpse;
int status;
while ((corpse = wait(&status)) != -1)
printf("Process %s: PID = %d, Child %d exited with status 0x%.4X\n", name, getpid(), corpse, status);
}
static void find_and_run(const char *name)
{
for (size_t i = 0; i < NUM_PSTREE; i++)
{
if (strcmp(name, pstree[i].name) == 0)
{
pid_t pid = fork();
if (pid < 0)
err_syserr("failed to fork(): ");
if (pid == 0)
be_childish(&pstree[i]);
return;
}
}
err_error("failed to find a subprocess named '%s'\n", name);
}
static void be_childish(const struct SubProcess *subproc)
{
printf("Process %s: PID = %d, PPID = %d\n", subproc->name, getpid(), getppid());
for (size_t i = 0; i < subproc->nkids; i++)
find_and_run(subproc->kids[i]);
printf("Process %s: PID = %d, %zu kids launched\n", subproc->name, getpid(), subproc->nkids);
wait_for_kids(subproc->name);
printf("Process %s: PID = %d, %zu kids finished\n", subproc->name, getpid(), subproc->nkids);
exit(subproc - pstree);
}
int main(void)
{
err_setarg0("pstree");
err_setlogopts(ERR_PID|ERR_MILLI);
be_childish(&pstree[0]);
return(EXIT_SUCCESS);
}
Here's the output from a sample run:
Process A: PID = 20101, PPID = 706
Process B: PID = 20102, PPID = 20101
Process A: PID = 20101, 2 kids launched
Process B: PID = 20102, 1 kids launched
Process C: PID = 20103, PPID = 20101
Process C: PID = 20103, 0 kids launched
Process C: PID = 20103, 0 kids finished
Process D: PID = 20104, PPID = 20102
Process A: PID = 20101, Child 20103 exited with status 0x0200
Process E: PID = 20105, PPID = 20104
Process E: PID = 20105, 0 kids launched
Process E: PID = 20105, 0 kids finished
Process D: PID = 20104, 2 kids launched
Process F: PID = 20106, PPID = 20104
Process D: PID = 20104, Child 20105 exited with status 0x0400
Process F: PID = 20106, 0 kids launched
Process F: PID = 20106, 0 kids finished
Process D: PID = 20104, Child 20106 exited with status 0x0500
Process D: PID = 20104, 2 kids finished
Process B: PID = 20102, Child 20104 exited with status 0x0300
Process B: PID = 20102, 1 kids finished
Process A: PID = 20101, Child 20102 exited with status 0x0100
Process A: PID = 20101, 2 kids finished
The error handling code is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c
and stderr.h
in the src/libsoq sub-directory.
Upvotes: 0
Reputation: 1574
A much simpler and easier-to-debug way to create process trees like this in C using fork()
is to create a function for each lettered process and have those functions call fork()
when appropriate. Using loops like you attempt in your proposed solution is possible, but very tricky and hard to read, especially since there doesn't appear to be a useful pattern in this tree.
This is an example of a function to perform the duties of A by calling fork()
twice to produce two distinct child processes, one to perform the duties of B, the other C.
#include <stdio.h>
#include <unistd.h>
void B(int parent){
printf("I am process B. My PID is %d, my parent's PID is %d\n",getpid(),parent);
//Do rest of B's job
}
void C(int parent){
printf("I am process C. My PID is %d, my parent's PID is %d\n",getpid(),parent);
//Do rest of C's job
}
void A(){
//A must fork off a process for B and for C
int A_pid = getpid();
int B_pid = fork();
int C_pid = 0;
if(B_pid == 0) //I'm the first child so I should call B()
B(A_pid);
else if(B_pid > 0){ //I'm the parent so I need to fork() again
C_pid = fork();
if(C_pid == 0) //I'm the second child so I should call C()
C(A_pid);
else if(C_pid > 0) //I'm still the parent
printf("I am process A. My PID is %d and I don't have a parent\n",A_pid);
else
;//Handle fork failing the second time
}
else
;//Handle fork failing the first time
//Do the rest of A's job, wait for children to do their thing etc.
kill(B_PID, SIGKILL);
kill(C_PID, SIGKILL);
}
int main(){
A();
}
Side note: It is very important to remember when using fork()
in UNIX-based operating systems that it can fail, and when it does it returns -1. This can cause very unexpected results if you do not catch and handle this error, because calling kill(-1,<signal>)
sends that signal to every process it has access to, which is likely not what you'd like.
Upvotes: 3
Reputation: 27
#include <unistd.h>
#include <signal.h>
void child(int index, int level, pid_t parent){ //this is going to be called recursively by all child processes
pid_t cpid[3]; int i = 0; pid_t Lparent = getpid();
if(index == 0 && level == 1){ //this is B
if( (cpid[i]=fork()) == 0){//D is going to execute this
child(i, 2, Lparent);
}
sleep(3);
for(i=0; i<1; i++){ //B will kill D
kill(cpid[i], SIGKILL);
}
printf("My PID: %d, My Parent's PID:%d\n", getpid(), parent);//this is B
sleep(7);
}
else if(index == 0 && level == 2){ //this is D
for(i = 0; i < 2; i++){
if((cpid[i]=fork()) == 0){
child(i, 3, Lparent);
}
}
sleep(5);
for(i=0; i<2; i++){
kill(cpid[i], SIGKILL);
printf("My PID: %d, My parent's PID:%d\n", getpid(), parent);
sleep(5);
}
}
else{
printf("My PID: %d, My Parent's PID:%d\n", getpid(), parent);
sleep(10);
}
}
int main(){
pid_t cpid[2];
pid_t root = getpid();
int i=0;
for(i=0; i<2; i++){
if( (cpid[i]=fork()) == 0){
child(i,1,root);
}
}
sleep(8);
for(i=0; i<2; i++){
kill(cpid[i],SIGKILL);
}
printf("My Pid: %d\n", getpid());
return 0;
}
Upvotes: 0