UNIX环境高级编程习题——第八章
来源:互联网 发布:大数据接入dpi 编辑:程序博客网 时间:2024/06/06 09:20
8.1 在图8-3 程序中,如果exit调用替代_exit调用,那么可能会使标准输出关闭,使printf返回-1.修改该程序以验证你所使用的系统上是否会产生这种错误。如果并非如此,你怎样处理才能得到类似结果呢?
为了方针子进程终止时关闭标准输出的行为,在调用exit之前加入下列代码行:fclose(stdout);
/************************************************************************* > File Name: ins8_3.c > Author: Elliot ************************************************************************/#include "apue.h"int globvar = 6; /* external variable in initialized data */#define SIZE 256intmain(void){ int var; /* automatic variable on the stack */ pid_t pid; int i; char buf[SIZE]; var = 88; printf("before vfork\n"); /* we don't flush stdio */ if ((pid = vfork()) < 0) err_sys("vfork error"); else if(pid == 0) { globvar++; /* child */ var++; /* modify parent's variables */ fclose(stdout); exit(0); } /* parent continue here */ i = printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var); sprintf(buf, "%d\n", i); write(STDOUT_FILENO, buf, strlen(buf)); exit(0);}
这里假设子进程调用exit函数时关闭标准I/O流,但不关闭文件描述符 STDOUT_FILENO。有些版本的标准I/O库会关闭与标准输出相关联的文件描述符从而引起write标准输出失败。着这种情况下,调用dup将标准输出复制到另一个描述符,write则使用新复制的文件描述符。
8.2 回忆图7-6中典型的存储空间布局。由于对应于每个函数调用的栈帧通常存储在栈中,并且由于调用vfork后,子进程运行在父进程的地址空间中,如果不是在main函数中而是在另一个函数中调用vfork,伺候子进程又从该函数返回,会发生什么?请编写一段测试程序对此验证。
/************************************************************************* > File Name: test8_2.c > Author: Elliot ************************************************************************/#include "apue.h"#define SIZE 15static void f1(void);static void f2(char *buf);intmain(void){ f1(); f2();}static voidf1(void){ if (vfork() < 0) err_sys("vfork error");}static voidf2(char *buf){ char buf[1000]; int i; for (i =0; i < 1000; i++) buf[i] = 0;}
函数f1调用vfork时,父进程的栈指针指向f1的函数的栈帧,vfork使得子进程先执行然后从f1返回,接着子进程调用f2,并且f2的栈帧覆盖了f1的栈帧,在f2中进程将自动变量buf的值设为0,即将栈中的1000个字节的值都设置为0。从f2返回后子进程调用_exit,这时栈中main栈帧一下的内容已经被f2修改了。然后,父进程从vfork调用后回复继续,并从f1返回。
8.4 当用 $ ./a.out 执行 图8-13 中的程序一次时,其输出是正确的。但是若从该进程按下列方式执行多次,则其输出不正确。
$ ./a.out ; ./a.out ; ./a.outoutput from parentooutput from parentouotuptut from childput from parentoutput from childutput from child
原因是什么?怎样才能更正此类错误?如果使子进程首先输出,还会发生此问题吗?
执行完第一个 ./a.out 之后父进程先终止还是子进程先执行,这两者执行的先后顺序出现竞争,依赖于内核调度。父进程先执行,会干扰上一个子进程的输出,导致输出混乱。如果子进程先输出,同理也有可能造成输出混乱。
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)命令以验证该进程是僵死进程。
/************************************************************************* > File Name: test8_6.c > Author: Elliot ************************************************************************/#include "apue.h"intmain(void){ pid_t pid; if ((pid = fork()) < 0) err_sys("fork error"); else if(pid == 0) exit(0); sleep(4); system("ps ao pid,ppid,state,tty,command"); exit (0);}
8.7 8.10节中提及POSIX.1要求在exec时关闭打开目录流。按下列方法对此验证:对根目录调用opendir,查看在你系统上实现的DIR结构,然后打印执行时关闭标志。接着打开同一目录读并打印执行时关闭标志。
验证流程:(1)opendir获取DIR结构指针。(2)用dirfd获取该打开目录的文件描述符。(3)用fcntl设置 F_GETFD 获取当前文件描述符标志。(4)fd传入一个缓冲区中,供子进程使用。(5)fork一个子进程,子进程exec另一个名为child的程序(6)child验证fd是否被关闭。
/************************************************************************* > File Name: test8_7.c > Author: Elliot ************************************************************************/#include "apue.h"#include <dirent.h>#include <fcntl.h>#include <sys/wait.h>intmain(void){ DIR *dir; int fd; int fd_flags; int status; char buf[10]; /* save fd */ pid_t pid; if ((dir = opendir("/")) == NULL) err_sys("opendir error"); if ((fd = dirfd(dir)) < 0) /* get fd */ err_sys("get dir fd error"); sprintf(buf, "%d\0", fd); if ((fd_flags = fcntl(fd, F_GETFD, 0)) < 0) err_sys("get fd flags error"); /* * prove: close the flags of FD_CLOEXEC */ if (fd_flags & FD_CLOEXEC) printf("close-on-exec is on\n"); else printf("close-on-exec is off\n"); if ((pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { execl("/home/king/LearningC/apue/Chapter8/child", "child", buf, NULL); exit(0); } if ((pid = waitpid(pid, NULL, 0)) < 0) err_sys("waitpid erro"); exit(0);}
/************************************************************************* > File Name: child.c > Author: Elliot ************************************************************************/#include "apue.h"#include <fcntl.h>intmain(int argc, char *argv[]){ int fd, fd_flags; sscanf(argv[1], "%d", &fd); if ((fd_flags = fcntl(fd, F_GETFD)) < 0) err_sys("get fd flags error"); if (fd_flags & FD_CLOEXEC) printf("close-on-exec is on\n"); else printf("close-on-exec is off\n"); exit(0);}
结果如下:
$ ./test8_7_2 close-on-exec is onget fd flags error: Bad file descriptor说明exec关闭打开目录流时,如果与打开目录流关联的该目录文件描述符标志中设置了 FD_CLOEXEC 标志,则会同时关闭与该流关联的文件描述符。
再进一步验证:关闭 FD_CLOEXEC 标志,子进程是否能再次与打开目录流关联的打开文件描述符
/************************************************************************* > File Name: test8_7.c > Author: Elliot ************************************************************************/#include "apue.h"#include <dirent.h>#include <fcntl.h>#include <sys/wait.h>intmain(void){ DIR *dir; int fd; int fd_flags; int status; char buf[10]; /* save fd */ pid_t pid; if ((dir = opendir("/")) == NULL) err_sys("opendir error"); if ((fd = dirfd(dir)) < 0) /* get fd */ err_sys("get dir fd error"); //sprintf(buf, "%d\0", fd); if ((fd_flags = fcntl(fd, F_GETFD, 0)) < 0) err_sys("get fd flags error"); /* * prove: close the flags of FD_CLOEXEC */ if (fd_flags & FD_CLOEXEC) printf("close-on-exec is on\n"); else printf("close-on-exec is off\n"); fd_flags &= ~FD_CLOEXEC; /* * 关闭FD_CLOEXEC标志后,执行exec函数,关闭打开的目录流,但不会关闭与该流相关的 * 文件描述符。 */ fcntl(fd, F_SETFD, fd_flags); sprintf(buf, "%d\0", fd); if ((pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { execl("/home/king/LearningC/apue/Chapter8/child", "child", buf, NULL); exit(0); } if ((pid = waitpid(pid, NULL, 0)) < 0) err_sys("waitpid erro"); exit(0);}
执行结果如下:
$ ./test8_7close-on-exec is onclose-on-exec is off
此时,子进程能正确打开与打开目录流关联的文件描述符fd。且是在fd的FD_CLOEXEC标志被关闭的前提下打开。
结论:系统默认保持 FD_CLOEXEC 标志打开,执行exec的时候会关闭打开目录的文件描述符。
- UNIX环境高级编程习题——第八章
- UNIX环境高级编程———第三章习题
- UNIX环境高级编程习题——第二章
- UNIX环境高级编程习题——第三章
- UNIX环境高级编程习题——第四章
- UNIX环境高级编程习题——第五章
- UNIX环境高级编程习题——第六章
- UNIX环境高级编程习题——第七章
- unix环境高级编程第八章读书笔记
- UNIX环境高级编程习题——第一章
- UNIX环境高级编程——第八章—进程控制
- Unix环境高级编程学习笔记(第八章)
- UNIX环境高级编程--第八章进程控制总结
- 《Unix高级环境编程》第八章 进程控制
- <<UNIX环境高级编程>>之第八章理解
- Unix环境高级编程 第三章习题答案
- unix高级环境编程 第二十章 数据库函数库:习题讨论
- unix环境高级编程第三章习题的一些拙见
- 程序员必须掌握的8大排序算法(四):堆排序
- mac下安装和使用brew
- hdu1150—Machine Schedule(最小点覆盖)
- Spring的配置及引用
- fprintf函数
- UNIX环境高级编程习题——第八章
- 牛客网 Wannafly模拟赛 矩阵 二分+hash矩阵
- UIView
- leetcode 235. Lowest Common Ancestor of a Binary Search Tree
- java 中的大数据类型(BigInteger和BigDecimal)
- Linux上安装wildfly-10.1.0.Final并部署iServer9D war包
- Spring Boot 学习——Controller的使用
- 《鸟哥的Linux私房菜 基础学习篇(第三版)》习题与解析(四)
- HDU ACM2014