Team BBL
Previous Page Next Page

Chapter 8

8.1

To simulate the behavior of the child closing the standard output when it exits, add the following line before calling exit in the child:

      fclose(stdout);

To see the effects of doing this, replace the call to printf with the lines

      i = printf("pid = %d, glob = %d, var = %d\n",
          getpid(), glob, var);
      sprintf(buf, "%d\n", i);
      write(STDOUT_FILENO, buf, strlen(buf));

You need to define the variables i and buf also.

This assumes that the standard I/O stream stdout is closed when the child calls exit, not the file descriptor STDOUT_FILENO. Some versions of the standard I/O library close the file descriptor associated with standard output, which would cause the write to standard output to also fail. In this case, dup standard output to another descriptor, and use this new descriptor for the write.

8.2

Consider Figure C.7.

Figure C.7. Incorrect use of vfork
#include "apue.h"

static void f1(void), f2(void);

int
main(void)
{
    f1();
    f2();
    _exit(0);
}

static void
f1(void)
{
    pid_t   pid;

    if ((pid = vfork()) < 0)
        err_sys("vfork error");
    /* child and parent both return */
}

static void
f2(void)
{
    char    buf[1000];       /* automatic variables */
    int     i;

    for (i = 0; i < sizeof(buf); i++)
        buf[i] = 0;
}

When vfork is called, the parent's stack pointer points to the stack frame for the f1 function that calls vfork. Figure C.8 shows this.

Figure C.8. Stack frames when vfork is called


vfork causes the child to execute first, and the child returns from f1. The child then calls f2, and its stack frame overwrites the previous stack frame for f1. The child then zeros out the automatic variable buf, setting 1,000 bytes of the stack frame to 0. The child returns from f2 and then calls _exit, but the contents of the stack beneath the stack frame for main have been changed. The parent then resumes after the call to vfork and does a return from f1. The return information is often stored in the stack frame, and that information has probably been modified by the child. After the parent resumes, what happens with this example depends on many implementation features of your UNIX system (where in the stack frame the return information is stored, what information in the stack frame is wiped out when the automatic variables are modified, and so on). The normal result is a core file, but your results may differ.

8.3

In Figure 8.13, we have the parent output first. When the parent is done, the child writes its output, but we let the parent terminate. Whether the parent terminates or whether the child finishes its output first depends on the kernel's scheduling of the two processes (another race condition). When the parent terminates, the shell starts up the next program, and this next program can interfere with the output from the previous child.

We can prevent this from happening by not letting the parent terminate until the child has also finished its output. Replace the code following the fork with the following:

     else if (pid == 0) {
         WAIT_PARENT();          /* parent goes first */
         charatatime("output from child\n");
         TELL_PARENT(getppid()); /* tell parent we're done */
     } else {
         charatatime("output from parent\n");
         TELL_CHILD(pid);        /* tell child we're done */
         WAIT_CHILD();           /* wait for child to finish */
     }

We won't see this happen if we let the child go first, since the shell doesn't start the next program until the parent terminates.

8.4

The same value (/home/sar/bin/testinterp) is printed for argv[2]. The reason is that execlp ends up calling execve with the same pathname as when we call execl directly. Recall Figure 8.15.

8.5

A function is not provided to return the saved set-user-ID. Instead, we must save the effective user ID when the process is started.

8.6

The program in Figure C.9 creates a zombie.

Figure C.9. Create a zombie and look at its status with ps
#include "apue.h"

#ifdef SOLARIS
#define PSCMD     "ps -a -o pid,ppid,s,tty,comm"
#else
#define PSCMD     "ps -o pid,ppid,state,tty,command"
#endif

int
main(void)
{
    pid_t    pid;

    if ((pid = fork()) < 0)
        err_sys("fork error");
    else if (pid == 0)      /* child */
         exit(0);

     /* parent */
     sleep(4);
     system(PSCMD);

     exit(0);
}

Zombies are usually designated by ps(1) with a status of Z:

     $ ./a.out
        PID  PPID S TT         COMMAND
       3395  3264 S pts/3      bash
      29520 3395  S pts/3      ./a.out
      29521 29520 Z pts/3      [a.out] <defunct>
      29522 29520 R pts/3      ps -o pid,ppid,state,tty,command

    Team BBL
    Previous Page Next Page