APUE第八章习题

8.1 在图8-3 程序中,如果exit调用替代_exit调用,那么可能会使标准输出关闭,使printf返回-1.修改该程序以验证你所使用的系统上是否会产生这种错误。如果并非如此,你怎样处理才能得到类似结果呢?
8-3代码

#include "apue.h"

int     globvar = 6;        /* external variable in initialized data */

int
main(void)
{
    int     var;        /* automatic variable on the stack */
    pid_t   pid;

    var = 88;
    printf("before vfork\n");   /* we don't flush stdio */
    if ((pid = vfork()) < 0) {
        err_sys("vfork error");
    } else if (pid == 0) {      /* child */
        globvar++;              /* modify parent's variables */
        var++;
        _exit(0);               /* child terminates */
    }

    /* parent continues here */
    printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar,
      var);
    exit(0);
}

为了方针子进程终止时关闭标准输出的行为,在调用exit之前加入下列代码行:
fclose(stdout);
有些版本的标准I/O库会关闭与标准输出相关联的文件描述符从而引起write标准输出失败。着这种情况下,调用dup将标准输出复制到另一个描述符,write则使用新复制的文件描述符。

#include "apue.h"

int     globvar = 6;        /* external variable in initialized data */

int
main(void)
{
    int     var;        /* automatic variable on the stack */
    pid_t   pid;
    int     i;
    int     nfd;
    char    buf[BUFSIZ];

    memset(buf, 0x00, sizeof(buf));
    var = 88;
    printf("before vfork\n");   /* we don't flush stdio */
    if ((pid = vfork()) < 0) {
        err_sys("vfork error");
    } else if (pid == 0) {      /* child */
        globvar++;              /* modify parent's variables */
        var++;
        dup2(STDOUT_FILENO, nfd);
        fclose(stdout);
        exit(0);                /* child terminates */
    }

    /* parent continues here */
    i = printf("pid = %ld, glob = %d, var = %d", (long)getpid(), globvar,
      var);
    sprintf(buf, "%d\n", i);
    write(nfd, buf, strlen(buf));
    exit(0);
}

运行结果:

before vfork
-1
得到类似结果

8.2 回忆图7-6中典型的存储空间布局。由于对应于每个函数调用的栈帧通常存储在栈中,并且由于调用vfork后,子进程运行在父进程的地址空间中,如果不是在main函数中而是在另一个函数中调用vfork,此后伺候子进程又从该函数返回,会发生什么?请编写一段测试程序对此验证。

#include "apue.h"

int globvar = 6;
int var = 88;

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

int main(void)
{
    f1();
    f2();
    _exit(0);
}
static void f1(void)
{
    pid_t pid;
    printf("before vfork\n");   /* we don't flush stdio */
    if ((pid = vfork()) < 0)
    {
        err_sys("vfork error");
    }
    else if (pid == 0)
    {
        globvar++;
        var++;
    }
    else
        sleep(5);
    printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar,
      var);
}
static void f2(void)
{
    printf("this is f2\n");
}

运行结果:

before vfork
pid = 4604, glob = 7, var = 89
this is f2
pid = 4603, glob = 7, var = 89
before vfork
pid = 4605, glob = 8, var = 90
pid = 4603, glob = 8, var = 90
Segmentation fault (core dumped)

vfork的子进程运行在父进程的空间里,vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。当子进程运行完,之后堆栈的内容已经被子进程修改了,返回错误的地址,导致父进程运行混乱,产生core文件。

8.4 当用 $ ./a.out 执行 图8-13 中的程序一次时,其输出是正确的。但是若从该进程按下列方式执行多次,则其输出不正确。

$ ./a.out ; ./a.out ; ./a.out
output from parent
ooutput from parent
ouotuptut from child
put from parent
output from child
utput from child

原因是什么?怎样才能更正此类错误?如果使子进程首先输出,还会发生此问题吗?

8-13 fork部分代码

if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        WAIT_PARENT();      /* parent goes first */
        charatatime("output from child\n");
    } else {
        charatatime("output from parent\n");
        TELL_CHILD(pid);
    }

输出混乱的原因是因为子进程需要等待父进程charatatime输出完,而父进程不需要等待子进程,所以子进程运行期间父进程可能已经终止了。而父进程终止后,shell会运行下一个程序,这就是为什么8-13采用./a.out ; ./a.out ; ./a.out输出混乱的原因。如果使子进程首先输出,更改代码:

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {
        charatatime("output from child\n");
        TELL_PARENT(getppid());
    } else {
        WAIT_CHILD();
        charatatime("output from parent\n");
    }

子进程先运行,所以不会出现上面情况。

8.5 在图8-20 所示的程序中,调用execl,指定pathname为解释器文件。如果将其修改为调用execlp,指定testinterp的filename,并且如果目录/home/sar/bin 是路径前缀,则运行该程序时,argv[2]的打印输出是什么?

argv[2]的输出和调用execl时一样,也是输出 /home/sar/bin/testinterp。
原因:execlp 在结束时调用execve,并且与直接调用execl的路径名相同。

8.6 编写一段程序创建一个僵死进程,然后调用system执行ps(1)命令以验证该进程是僵死进程。

#include "apue.h"

int main(int argc, char *argv[])
{
    pid_t pid;
    if ((pid = fork()) < 0)
        err_sys("fork error");
    else if (pid == 0)
    {
    exit(0);
    }

    sleep(3);
    system("ps -o pid,ppid,state,tty,command");
    exit(0);
}

运行结果:

PID PPID S TT COMMAND
3158 3150 S pts/5 bash
4240 3158 S pts/5 ./8_6
4241 4240 Z pts/5 [8_6]
4242 4240 S pts/5 sh -c ps -o pid,ppid,state,tty,command
4243 4242 R pts/5 ps -o pid,ppid,state,tty,command
8.7 8.10节中提及POSIX.1要求在exec时关闭打开目录流。按下列方法对此验证:对根目录调用opendir,查看在你系统上实现的DIR结构,然后打印执行时关闭标志。接着打开同一目录读并打印执行时关闭标志。

对根目录调用opendir,查看在你的系统上实现的DIR结构,然后打印执行时关闭标志。接着open同一目录读取并打印执行时关闭标志
代码:

#include "apue.h"
#include
#include

int main(int argc, char *argv[])
{
    DIR *dirp;
    int dir_fd;
    int val;
    int fd;

    if ((dirp = opendir("/")) == NULL)
        err_sys("opendir error");
    if ((dir_fd = dirfd(dirp)) < 0)
        err_sys("dirfd error");
    if ((val = fcntl(dir_fd, F_GETFD)) < 0)
        err_sys("fcntl dir_fd error");
    if (val & FD_CLOEXEC)
        printf("close-on-exec flag is on\n");
    else
        printf("close-on-exec flag is off\n");
    if (closedir(dirp) < 0)
        err_sys("closedir error");

    if ((fd = open("/", O_DIRECTORY)) < 0)
        err_sys("open dir error");
    if ((val = fcntl(fd, F_GETFD)) < 0)
        err_sys("fcntl fd error");
    if (val & FD_CLOEXEC)
        printf("close-on-exec flag is on\n");
    else
        printf("close-on-exec flag is off\n");
    return 0;
}

运行结果:

close-on-exec flag is on
close-on-exec flag is off
验证了opendir函数会设置FD_CLOEXEC标志,而open函数不会

你可能感兴趣的:(apue学习笔记)