1

In this program, mulproc.c , I am trying to run the executables from two programs I made (one counts the amount of alphabetic characters in a file and the other one counts five specific special characters). I am trying to make a parent process (just mulproc.c in this case) that runs these two programs each in their own child process, so just create two child processes from a parent basically. The two programs each have their own output, but around their respective outputs, I also want to output two messages for each, indicating when it starts and when it ends. However, I am getting incorrect and varying outputs with each different attempt (I do not want to post them all here). Outputs of my two programs are even being written in between each other, so I believe I may be utilizing the waitpid() function incorrectly. Whats more is I am having trouble printing out the correct child processes PID's in the parent process. Please ignore the two "Child Process" number labels for debugging purposes being out of place. Here is mulproc.c ...

#include <stdio.h>
#include "count.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h> 
#include <stdbool.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

int main( int argc, char *argv[] )
{       
    pid_t pid1;
    pid_t pid2;
    int status1;
    int status2;

    pid1 = fork();

    if ( pid1 < 0 )
    {
        perror( "ERROR! Fork failed!" );
        exit(1);
    }

    if ( pid1 == 0 )
    {
        // CHILD PROCESS CODE

        printf( "\nChild Process 2:\npid :%d\nppid:%d\n\n", getpid(), getppid() );

        printf( "CHILD <PID: %d> process is executing testspecial program!\n", getpid() );

        char *specialchar[] = { "./testspecial" , NULL };
        execv( specialchar[0], specialchar );   
    }

    if ( pid1 > 0 )
    {

        pid2 = fork();

        if ( pid2 == 0 )
        {
            // ANOTHER CHILD PROCESS CODE

            printf( "\nChild Process 1:\npid :%d\nppid:%d\n\n", getpid(), getppid() );

            printf( "CHILD <PID: %d> process is executing testalphabet program!\n", getpid() );

            char *alphabetchar[] = { "./testalphabet" , NULL };
            execv( alphabetchar[0], alphabetchar );
        }

        else if ( pid2 > 0 )
        {
            // PARENT PROCESS CODE

            printf( "\nParent Process:\npid:%d\nppid :%d\n", getpid(), getppid() );

            // if child1 terminated...

            if ( waitpid( pid1, &status1, 0 ) == pid1 )
            {
                printf( "CHILD <PID: %d> process has done with testalphabet program! See the results above!\n", getpid() );
            }

            // if child2 terminated...

            if ( waitpid( pid2, &status2, 0 ) == pid2 )
            {
                printf( "CHILD <PID: %d> process has done with testspecial program! See the results above!\n", getpid() );
            }
        }

    }


    return 0;
    
}

and here is just ONE of the incorrect outputs...

Parent Process:
pid:3166
ppid :3149

Child Process 1:
pid :3168
ppid:3166

CHILD <PID: 3168> process is executing testalphabet program!

Child Process 2:
pid :3167
ppid:3166

CHILD <PID: 3167> process is executing testspecial program!
A -> 0
B -> 0
C -> 0
D -> 0
E -> 0
F -> 0
G -> 0
H -> 3
I -> 0
J -> 0
K -> 0
L -> 0
M -> 0
N -> 0
O -> 0
P -> 0
Q -> 0
, -> 1
R -> 0
S -> 0
T -> 0
. -> 1
U -> 0
: -> 1
V -> 0
; -> 1
W -> 0
! -> 1
X -> 0
Y -> 0
Z -> 3
a -> 0
b -> 0
c -> 0
d -> 0
e -> 0
f -> 0
g -> 0
h -> 3
i -> 0
j -> 0
k -> 0
CHILD <PID: 3166> process has done with testalphabet program! See the results above!
l -> 0
m -> 0
n -> 0
o -> 0
p -> 0
q -> 0
r -> 0
s -> 0
t -> 0
u -> 0
v -> 0
w -> 0
x -> 0
y -> 0
z -> 0
CHILD <PID: 3166> process has done with testspecial program! See the results above!

I WANT the output to look something like this...

CHILD <PID: 3168> process is executing testalphabet program!
A -> 0
B -> 0
C -> 0
...
...
...
x -> 0
y -> 0
z -> 0
CHILD <PID: 3168> process has done with testalphabet program! See the results above!
CHILD <PID: 3167> process is executing testspecial program!
, -> 1
. -> 1
: -> 1
; -> 1
! -> 1
CHILD <PID: 3167> process has done with testspecial program! See the results above!

Really really appreciate it if anyone could clear up my mistakes here...I am brand new to using system calls like these, so hopefully I am not messing up too much in this instance.

PluffTed
  • 53
  • 5
  • regarding: `#include "count.h"` We cannot reproduce the problem, because the posted code does not compile. Please post the contents of `count.h` – user3629249 Jul 12 '20 at 17:28
  • OT: regarding: `int main( int argc, char *argv[] )` The parameters are not used. This results in the compiler outputting two warning messages about unused parameters. A simple fix would be to use the other valid signature for `main()`. `int main( void )` – user3629249 Jul 12 '20 at 17:34
  • Careful examination shows that NOTHING in the `count.h` header file is being used. Therefore, that header file should not be included. – user3629249 Jul 12 '20 at 17:35
  • what happens when `pid2` is <0 ? – user3629249 Jul 12 '20 at 17:38
  • OT: it is a very poor programming practice to include header files those contents are not used: Suggest removing: `#include ` and `#include ` and `#include ` and `#include ` and `#include ` – user3629249 Jul 12 '20 at 17:44
  • regarding this kind of statement: `execv( specialchar[0], specialchar );` the call to `execv()` can fail, then the child will be executing code it should not. Suggest: `execv( specialchar[0], specialchar ); perror( "execv for specialchar failed" ); exit( EXIT_FAILURE );` – user3629249 Jul 12 '20 at 17:47

3 Answers3

2

If you don't want the processes to run in parallel, then you have to wait for the first one to finish before launching the second one. So written in pseudo-code, rather than do this:

pid1 = launch_proc1();
pid2 = launch_proc2();

waitpid(pid1);
waitpid(pid2);

You have to do:

pid1 = launch_proc1();
waitpid(pid1);

pid2 = launch_proc2();    
waitpid(pid2);

How to translate the pseudocode into proper c is left as an exercise to the reader.

EDIT: Some clarification on launc_procX:

launc_procX is supposed to implement the fork/exec combo.

int launc_proc1()
{
    int pid = fork();

    if ( pid < 0 )
    {
        perror( "ERROR! Fork failed!" );
        exit(EXIT_FAILURE);
    }

    else if (pid == 0)
    {
        // CHILD PROCESS CODE

        printf( "\nChild Process 2:\npid :%d\nppid:%d\n\n", getpid(), getppid() );

        printf( "CHILD <PID: %d> process is executing testspecial program!\n", getpid() );

        char *specialchar[] = { "./testspecial" , NULL };
        execv( specialchar[0], specialchar );

        // Handle failure of exec (this is important !!!!!)
        perror( "ERROR! exec failed!" );
        exit(EXIT_FAILURE);
    }
    // because execv is supposed to never return,
    // only parent is able to reach this point

    return pid;
}

Naturally you can combine launch_proc1 and launch_proc2 into a single int launch_proc(const char *cmd)

HAL9000
  • 2,138
  • 1
  • 9
  • 20
  • I know you said this is pseudocode but can you elaborate on launch_proc() ? – PluffTed Jul 11 '20 at 21:59
  • Hey thank you so much for elaborating I really appreciate it.....one more question though.....inside launc_proc1() after the execv(), I am trying to output a message to the user before the function (process in this case) exits. However, my message does not even output and I think it is because execv() is inhibiting me from this. Is there a way around this? – PluffTed Jul 12 '20 at 03:06
  • when `execv()` is successful, it doe NOT return. – user3629249 Jul 12 '20 at 17:55
1

@HAL9000 is right,

but for the sake of brevity I will rephrase:

after your first fork, you only need to put:

waitpid(YOUR_CHILD_PID, NULL, 0);

in the parent process BEFORE you fork again.

generally, processes can be created in a loop. where after each fork, in the parent process (where pid > 0), you wait on the child that was just created.

otherwise, you have a race condition (a race between your processes), and the result is your undefined behavior.

you get a different output each time depending on the order in which the kernel schedules the processes.

I think you know this. the main issue you have is that you are creating a second process before waiting for the first. so the order the processes are scheduled is undefined.

1

If you do not want the outputs of two child processes to be intermixed, in this case, wait for first child process to complete, and then, start the next. If you want the output of two concurrent processes should not be mixed, in general, make a special process named, say, print_process. Instead of printing directly, a process should send a message to the print_process. And, the print_process, should write on the terminal.

kjohri
  • 1,166
  • 11
  • 10