目录
一、标准C库的 IO 函数
标准C库函数写的代码,可以移植到其他操作系统。 但是基于操作系统的IO函数,需要依赖运行的操作系统。
二、标准C库IO 和 Linux系统IO的关系
用户程序 通过调用 C的标准库中 ,读函数,写函数。 函数底层会调用操作系统的API,对磁盘文件进行读写操作。
三、虚拟地址空间
程序运行起来是一个进程,系统给程序创建了进程空间,设想成 虚拟地址空间。创建一个虚拟空间实际上并不是创建空间,而是创建映射函数所需要的相应的数据结构。为了解决程序加载内存的问题,用来解释程序内存的模型。 虚拟地址空间模型如下图:
虚拟地址空间是由CPU来决定的,32位的计算机,理想的分配的虚拟地址空间大小是2^32,大约是4G。64位的计算机,分配的虚拟地址空间大小是2^48。 4G是理想的内存空间。 实际占用 物理内存,不会占用4G,是由CPU来管理。虚拟地址空间 会被CPU的逻辑单元 MMU映射到 内存物理单元。
内存空间的分布如上图, 内存分为 用户区 和 内核区 。
用户区的空间包括:
受保护的地址(0~4k)、.txt区代码段、 .data区域、 .bss区域、共享库、栈空间、命令行参数、 环境变量。
受保护的地址:
NULL,nullptr都是在该地址空间。
.txt区代码段:
代码段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令
.data区域:
已初始化的全局变量 和 静态局部变量。
.bss区域:
未初始化的全局变量 和 静态局部变量。
.data区和.bss区 称为全局数据区。
堆空间:
从低地址往 高地址存储。new,malloc创建的数据存放在堆中。
共享库:
是 动态链接库的文件。如C标准库函数(fread、fwrite、fopen等)和Linux系统I/O函数,它们都是动态库函数,其中C标准库函数都被封装在了/lib/libc.so库文件中,都是二进制文件。这些动态库函数都是与位置无关的代码,即每次被加载进入内存映射区时的位置都是不一样的,因此使用的是其本身的逻辑地址,经过变换成线性地址(虚拟地址),然后再映射到内存。而静态库不一样,由于静态库被链接到可执行文件中,因此其位于代码段,每次在地址空间中的位置都是固定的。
栈空间:
从高地址往低地址存储。像局部变量,函数栈等都是存在该空间。
命令行参数:
执行程序,输入的命令行参数,存放在该内存空间中。
环境变量:
Linux中,输入env 命令,就可以看到设置的环境变量,这些环境变量,存放在该内存空间中。
内核区:
用户没有内核区操作权限。如果想操作内核数据,只能通过系统API调用。进程控制块 PCB存放于内核中进行管理。
四、文件描述符
文件占用磁盘空间,打开了文件,是怎么找到文件,然后进行读写的呢? 因为有文件描述符,通过文件描述符,来查找相应的文件。
文件描述符是在进程的内核区,在进程控制块PCB中。每个进程都会有一个默认的文件描述符表,这个表中存储文件描述符的数组,最大是1024,因为一个进程能打开的文件,最多是1024个。
STDIN_FILENO 标准输入,STDOUT_FILENO标准输出,STDERR_FILENO标准错误 ,都是默认打开的状态。对应当前绑定的默认终端,因为输入,输出和错误 ,都会通过当前终端来操作或显示。
一个文件可以被打开多次,文件描述符是不同的。
Linux 中的 文件描述符的数据结构:
其中 _fileno 就是 文件描述符的序号,它来自于操作系统。
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
五、Linux系统IO函数
int open(const char* pathname, int flags);
int open(const char* pathname, int flags, mode_t mode);
int close(int fd);
ssize_t read(int fd, void* buf, size_t count);
ssize_t write(int fd, const void* buf,size_t count);
off_t lseek(int fd,off_t offset, int whence);
int stat(const char* pathname, struct stat* statbuf);
int lstat(const char* pathname, struct stat* statbuf);
六、stat结构体
struct stat {
dev_t st_dev; // 文件的设备编号
ino_t st_ino; // 节点
mode_t st_mode; // 文件的类型和存取的权限
nlink_t st_nlink; // 连到该文件的硬连接数目
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
dev_t st_rdev; // 设备文件的设备编号
off_t st_size; // 文件字节数(文件大小)
blksize_t st_blksize; // 块大小
blkcnt_t st_blocks; // 块数
time_t st_atime; // 最后一次访问时间
time_t st_mtime; // 最后一次修改时间
time_t st_ctime; // 最后一次改变时间(指属性)
};
七、st_mode变量
八、Linux系统函数
系统函数的功能,可以通过命令 man 2 function_name ,查看函数的信息。
1.文件属性操作函数
int access(const char* pathname, int mode);
int chmod(const char* filename, int mode);
int chown(const char* path, uid_t owner, gid_t group);
int truncate(const char* path, off_t length);
2.目录操作函数
int rename(const char* oldpath, const char* newpath);
int chdir(const char* path);
char* getcwd(char* buf, size_t size);
int mkdir(const char* pathname, mode_t mode);
int rmdir(const char* pathname);
3.目录遍历函数
DIR* opendir(const char* name);
struct dirent* readdir(DIR* dirp);
int closedir(DIR* dirp);
struct dirent
{
// 此目录进入点的inode
ino_t d_ino;
// 目录文件开头至此目录进入点的位移
off_t d_off;
// d_name 的长度, 不包含NULL字符
unsigned short int d_reclen;
// d_name 所指的文件类型
unsigned char d_type;
// 文件名
char d_name[256];
};
4.dup、 dup2函数
int dup(int oldfd);/*复制文件描述符*/
int dup2(int oldfd, int newfd);/*重定向文件描述符*/
5.fcntl函数
复制文件描述符,设置/获取文件的状态标志