【牛客网C++服务器项目学习】Day3-GDB、I/O库函数、虚拟内存、文件描述符
项目学习地址:【牛客网C++服务器项目学习】
day3
1.GDB
什么是GDB?
- GDB 是由GNU 软件系统社区提供的调试工具,同GCC 配套组成了一套完整的开发环境,GDB 是Linux 和许多类Unix 系统中的标准开发环境。
- 一般来说,GDB 主要帮助你完成下面四个方面的功能:
1.启动程序,可以按照自定义的要求随心所欲的运行程序
2.可让被调试的程序在所指定的调置的断点处停住(断点可以是条件表达式)
3.当程序被停住时,可以检查此时程序中所发生的事
4.可以改变程序,将一个BUG 产生的影响修正从而测试其他BUG
gcc -g -Wall program.c -o program
-g
选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb 能找到源文件。
GDB常用命令(太多了,略,太原始了,现如今的开发接触不到的)
-
list指令,查看代码。在gdb打开的文件中,用list查看代码。可以指定行数,指定函数名,指定文件名等操作。
-
break指令,设置断点。break 行号/函数名/文件名,行号/文件名,函数名
- 还可以设置条件断点:比如在for循环中,设置断点停在循环变量等于某个值时。
-
info指令,查看断点。
-
delete指令,删除断点。删除的是断点编号。
-
disable指令,失效断点。失效的是断点编号。
如果你有一定的编程经验,你肯定会在IDE中使用过打断点和调试了。现在的vscode、vs早已把GDB的指令集成在GUI上了。但是对于没有图形界面的,例如CentOS操作系统,这种使用命令的GDB调试方法,似乎是唯一的选择。
具体指令,详见下面的截图
2.标准C I/O 和Linux系统I/O函数的对比
这一节算是一个科普介绍向,帮助我们理解C语言中的I/O函数和Linux的I/O函数之间,是一个包含与被包含的关系。C语言的I/O函数是对Linux的I/O进行了一次包装,最大的差异,在于C语言设置了一个缓冲区,每次调用函数写数据时,都是先写入到缓冲区中,只有缓冲区满或者强制刷新缓冲区时,才会把数据写入到磁盘/显示器等
这个图,画的挺好
3.虚拟地址空间
Linux系统中的几个地址:
虚拟内存是操作系统的内存管理部分的内容,虚拟内存技术的出现是为了解决程序大于内存的问题,可以让操作系统“放大”现在的内存空间,例如,系统本身只有2G的内存,但是通过虚拟内存的地址映射、内存段的换入换出技术,可以实现对4G或者更大的内存空间的操作。(如果大家对如何在Linux系统中,通过虚拟地址寻找物理地址感兴趣的话,可以查看我写的 这篇文章)
一个程序,各部分,在内存中的位置图,画的挺清晰的
4.文件描述符
我们知道在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件、目录文件、链接文件和设备文件。在操作这些所谓的文件的时候,我们每操作一次就找一次名字,这会耗费大量的时间和效率。所以Linux中规定每一个文件对应一个索引,这样要操作文件的时候,我们直接找到索引就可以对其进行操作了。
文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4…
Linux内核对所有打开的文件有一个文件描述符表格,表格的大小为1024。(其中前三个被标准输入、标准输出、标准错误占据了。)里面存储了每个文件描述符作为索引与一个打开文件相对应的关系,简单理解就是下图这样一个数组,文件描述符(索引)就是文件描述符表这个数组的下标,数组的内容就是指向一个个打开的文件的指针。
上面只是简单理解,实际上关于文件描述符,Linux内核维护了3个数据结构:
-
进程级的文件描述符表
-
系统级的打开文件描述符表
-
文件系统的i-node表
一个 Linux 进程启动后,会在内核空间中创建一个 PCB 控制块,PCB 内部有一个文件描述符表(File descriptor table),记录着当前进程所有可用的文件描述符,也即当前进程所有打开的文件。
进程级的描述符表的每一条记录了单个进程所使用的文件描述符的相关信息,进程之间相互独立,一个进程使用了文件描述符3,另一个进程也可以用3。
除了进程级的文件描述符表,系统还需要维护另外两张表:打开文件表、i-node 表。这两张表存储了每个打开文件的打开文件句柄(open file handle)。一个打开文件句柄存储了与一个打开文件相关的全部信息。
系统级的打开文件描述符表:
-
当前文件偏移量(调用read()和write()时更新,或使用lseek()直接修改)
-
打开文件时的标识(open()的flags参数)
-
文件访问模式(如调用open()时所设置的只读模式、只写模式或读写模式)
-
与信号驱动相关的设置
-
对该文件i-node对象的引用,即i-node 表指针
文件系统的i-node表:
-
文件类型(例如:常规文件、套接字或FIFO)和访问权限
-
一个指针,指向该文件所持有的锁列表
-
文件的各种属性,包括文件大小以及与不同类型操作相关的时间戳
文件描述符、打开的文件句柄以及i-node之间的关系如下图:
这就说明:
-
同一个进程的不同文件描述符可以指向同一个文件(在一个进程中多次打开同一个文件);
-
不同进程可以拥有相同的文件描述符;
-
不同进程的同一个文件描述符可以指向不同的文件(一般也是这样,除了 0、1、2 这三个特殊的文件);
-
不同进程的不同文件描述符也可以指向同一个文件。
越12: process.log有问题,不建议按这个做,全是错
faft: 要怎么改阿,我一改就linux0.11j进不去了
找不到591: 删完注释之后再次编译,也是成功的,退出来./run竟然打不开bochs的输入界面,输
coldcodacode: 你好,请问能租你的牛客帐号看这个课程视频吗,这个视频是付费的
开始努力的阿凡: 谢谢哥 明白了点