《博学谷C++》四.1-4Linux文件和系统调用

这一节只学习了一部分,放弃了一些文件操作函数。因为感觉自己目前最需要学的是系统等相关的东西(进程间通信、线程、soket等),学怎么用这些函数也不难。

1.系统调用

程序等,通过统一系统调用接口来进行对内核资源的使用。这样做是为了保护系统资源。

shell、库函数、应用程序都可以进行系统调用。但不是所有操作都需要进行系统调用,例如字符串处理,不需要系统调用即可实现。

为了实现权限控制和资源保护,操作系统分为了两种运行级别:用户态内核态。用户态下的进程访问受限,而内核态没有限制,如驱动程序等。通过系统中断,实现用户态和内核态之间的切换。

2.系统调用和库函数

同:库函数和系统调用都是函数;

异:一些库函数不需要进行系统调用;另一些需要。

3.C库中IO函数工作流程

为什么需要缓冲区?

读写机械硬盘很慢,使用缓冲区可以减少对机械硬盘的读写、减少IO系统调用次数,提高访问效率。

3.1 errno变量

errno变量是定义在#include <errno.h>中的全局变量,可以直接使用,用于出错之后打印错误日志,方便排查错误。

全部错误号位于/usr/include/asm-generic/errno.h

3.2 strerror(errno)函数

输入errno变量,输出对应的错误信息。

3.3 perror()函数

类似于strerror,但不需要输入错误号,直接输出最后一次的错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(void) {
FILE *fp = fopen("test.txt", "r");
if (fp ==NULL){
printf("open file error!\n");
printf("error no is %d\n",errno);
printf("Error msg: %s\n",strerror(errno));
perror("Error msg");

}
return 0;
}

4.虚拟地址空间

32位机器中,进程虚拟地址空间为4G(= 2^32 ),实际内存地址空间没有这么大。

MMU:将虚拟的地址转化为物理地址。

这样做的好处在于:

  • 进程隔离,更好的保护系统安全运行
  • 屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址

静态局部变量放在.data区。

栈区:超出作用域范围后,变量消亡;

堆区:动态分配内存。

上面两个都不符合静态局部变量的特点。

查看栈空间大小:(一般10M左右)

  1. 递归测试;
  2. 使用命令查看。

栈地址是从高到低,而堆地址相反,从低到高。

只读数据段即常量区。

运行程序时,会将程序加载到内核区中。因此程序运行中即使强制(从硬盘)删除文件,程序也仍然还在运行。

内核区中,用户无权进行操作。

5. 文件描述符

打开现存文件或新建文件时,系统(内核)会返回一个文件描述符。

文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。

每个进程都有一张文件描述符的表,默认会打开标准输入、输出、错误输出。打开新的文件会打开新的文件描述符,最多打开1024个文件。

6.阻塞和非阻塞

读写常规文件是不会阻塞的,不管读写多少字节,一定会在有限时间内返回。

而向终端设备或网络写则不一定:如读数据时,如果终端没有输入的数据、没有接收到数据包,那么将会一直等待数据;如果写满后没有及时获得新的写入空间,那么写数据也会阻塞。阻塞时间不确定。

【注意】阻塞与非阻塞是对于文件而言的,而不是指read、write等的属性。