【Chapter8*编程总结*】gdb跟踪堆栈信息与重申标准IO与文件IO基本区别

写在前面

在课后习题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)之前手动关闭标准输出。

一、关闭标准IO出现段错误

附代码:

//测试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

好了,段错误是我们的老朋友了,看这次如何将他拿下!

二、GDB查看堆栈信息

最近新学了一招,叫bt,也就是backtrace的缩写,回溯的意思,通过它可以直接查看程序中的栈帧。

2.1 打开core dump功能

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

2.2 生成core文件

打开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

2.3 跟踪堆栈

参考: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         }

我是小笨蛋!

三、文件IO和标准IO

明显的,fclose是关闭标准输出流,但参数却是文件描述符,,,
看来,文件描述符和流给搞混了。

1、文件描述符

文件IO对应文件描述符,int型,每一个进程都有对应的3个文件描述符:

         标准输入  STDIN_FILENO      0
         标准输出  STDOUT_FILENO     1
         标准错误  STDERR_FILENO     2

定义,范围在0~OPEN_MAX-1之间,OPEN_MAX表示最大打开文件个数,一般是64。

文件IO的操作有open、read、write、lseek、close函数,在用户进程中不带缓冲

2、流(文件指针)

标准IO对应流,也就是文件指针,FILE* 类型,每一个进程有三个预定义的流:

       标准输入 stdin
       标准输出 stdout
       标准错误 stderr

是在中定义的,在用户进程中是带缓冲的,包括全缓冲、行缓冲、不带缓冲

标准IO的操作有fopen、fclose、getc/fgetc、gets/fgets、puts/fputs、fread、fwrite、ftell、fseek、printf等

如果想获得流关联的文件描述符,可使用fileno

你可能感兴趣的:(Unix环境编程)