在课后习题8-1,使用vfork创建子进程,子进程使用_exit(0)退出时不会执行清理程序,若换成exit(0)会执行终止处理程序和标准IO清理程序。这牵扯到标准IO的清理过程是怎样实现的:
1、冲洗缓冲区,将缓冲区数据输出,与exit(0)运行结果一样
2、关闭标准IO流,由于子进程在exit之前与父进程共享同一地址空间,则父进程标准IO被关闭,没有输出。
大多数Unix系统exit(0)实现是只冲洗缓冲区,不会关闭标准IO流,因为进程即将终止,内核将关闭所有打开的文件描述符。exit(0)不会多次一举。
在我的机器上验证确实如此,只清洗缓冲区,不会关闭标准IO。
如果要使得父进程没有输出,怎么做呢?很简单,在exit(0)之前手动关闭标准输出。
附代码:
//测试vfork父子进程的输出
2
3 #include"apue.h"
4 #include
5
6 int globvar = 6;
7
8 int main(void)
9 {
10 int var;
11 pid_t pid;
12
13 var = 88;
14 printf("before vfork\n");
15
16 if((pid=vfork())<0){
17 err_sys("创建子进程失败!\n");
18 }
19 else if(pid==0){//子进程
20 globvar++;
21 var++;
22
23 if(fclose(STDOUT_FILENO)!=0){
24 err_sys("关闭标准输出失败!\n");
25 }
26 exit(0);
27 }
28 //父进程
29 printf("pid=%ld,glob=%d,var=%d\n",pid,globvar,var);
30
31 return 0;
32 }
运行:
fairy@ubuntu:~/Unix_Code/Chapter8$ ./8-1
before vfork
Segmentation fault
好了,段错误是我们的老朋友了,看这次如何将他拿下!
最近新学了一招,叫bt,也就是backtrace的缩写,回溯的意思,通过它可以直接查看程序中的栈帧。
core是什么东西呢?它是依据linux提供的core dump机制,在程序发生崩溃时,内存映像会转存在磁盘上,也就是core文件。在可执行文件中有对core文件调试的符号表信息,使用gdb+可执行文件+core即可还原程序发生错误时的堆栈情况。
理论基础就到这里,那core文件怎么产生呢?大多数系统默认是关闭core dump功能的,通过ulimit -a命令查看。
fairy@ubuntu:~/Unix_Code/Chapter8$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
...
第一个core file size大小是0,说明该功能是关闭的。如何打开呢?很简单,更改core file size大小即可。一般使用ulimit -c umlimited。
ulimit -c 0 不产生core文件
ulimit -c 100 设置core文件最大为100k
ulimit -c unlimited 不限制core文件大小
设置成功后,再查看ulimit -a:
fairy@ubuntu:~/Unix_Code/Chapter8$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
...
表示设置成功!
注意:ulimit -c unlimited更改的是当前进程的资源情况,如果该进程关闭,其他进程若想使用core文件,仍需重新设置。一劳永逸的方法是在shell的启动脚本/etc/bashrc中设置。
参见文章:https://blog.csdn.net/star_xiong/article/details/43529637
打开core dump之后,使用gdb,运行时发生段错误就可顺带生成core文件
fairy@ubuntu:~/Unix_Code/Chapter8$ gcc -g 8-1.c -o 8-1
fairy@ubuntu:~/Unix_Code/Chapter8$ ./8-1
before vfork
Segmentation fault
OK,成功失败了~查看当前路径,发现多了core文件。8-1是可执行文件
fairy@ubuntu:~/Unix_Code/Chapter8$ ls
8-1 8-1.c core
参考:https://blog.csdn.net/learnhard/article/details/4879834
使用gdb 可执行文件名 core就可以进入调试状态:
fairy@ubuntu:~/Unix_Code/Chapter8$ gdb 8-1 core
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
....
[New LWP 3858]
[New LWP 3857]
Core was generated by `./8-1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 _IO_new_fclose (fp=0x1) at iofclose.c:53
53 iofclose.c: No such file or directory.
---Type to continue, or q to quit---
倒数第二行已经显示了错误,在标准IO出现的错误,要想看的更清楚,我们return继续,使用bt:
---Type to continue, or q to quit---return
[Current thread is 1 (LWP 3858)]
(gdb) bt
#0 _IO_new_fclose (fp=0x1) at iofclose.c:53
#1 0x000000000040108b in main () at 8-1.c:23
完美,一共两个栈帧,一个是main函数的,一个是fclose的,一般库函数是没啥毛病的,剩下就是main中调用出现错误,直接定位到8-1.c的第23行。
23 if(fclose(STDOUT_FILENO)!=0){
24 err_sys("关闭标准输出失败!\n");
25 }
我是小笨蛋!
明显的,fclose是关闭标准输出流,但参数却是文件描述符,,,
看来,文件描述符和流给搞混了。
文件IO对应文件描述符,int型,每一个进程都有对应的3个文件描述符:
标准输入 STDIN_FILENO 0
标准输出 STDOUT_FILENO 1
标准错误 STDERR_FILENO 2
在
文件IO的操作有open、read、write、lseek、close函数,在用户进程中不带缓冲
标准IO对应流,也就是文件指针,FILE* 类型,每一个进程有三个预定义的流:
标准输入 stdin
标准输出 stdout
标准错误 stderr
是在
标准IO的操作有fopen、fclose、getc/fgetc、gets/fgets、puts/fputs、fread、fwrite、ftell、fseek、printf等
如果想获得流关联的文件描述符,可使用fileno