c/c++:文件IO(Linux系统IO、C标准库IO、标准C 库IO和 Linux系统IO的关系、虚拟地址空间、文件描述符表和文件描述符、dup,dup2,fcntl函数)

目录

一、Linux系统IO和C标准库IO

1.1 标准C库IO函数

1.2 标准C 库IO和 Linux系统IO的关系

二、 虚拟地址空间

三、文件描述符表和文件描述符

6.1 dup和dup2函数

dup  复制文件描述符

dup2 重定向文件描述符

fcntl

四、Linux系统IO函数

五、Linux其他系统函数


 

 

一、Linux系统IO和C标准库IO

1.1 标准C库IO函数

 

1.2 标准C 库IO和 Linux系统IO的关系

标准C 库IO:带缓冲区

Linux系统IO:不带缓冲区,(有些书上说Linux系统有缓存,那是内核的缓存,不是IO的缓存)

 

 

 

二、 虚拟地址空间

下图以32位机为例,2的32次方为4G,64位机是2的64次方,太大了不好画。

 

三、文件描述符表和文件描述符

  • 文件描述符(file descriptor)通常是一个小的非负整数,内核用以标识一个特定进程正在访问的文件。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
  • 每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。

文件描述符表默认大小: 1024,每个进程启动之后, 都有一个文件描述符表,所以每个进程默认能打开的文件个数: 1024
前三个文件文件描述符是默认被使用了的:  - 标准输入 -> 0, 标准输出 -> 1, 标准错误 -> 2

除去被占用的每个进程默认能打开的文件个数: 1021

 

6.1 dup和dup2函数

  • dup  复制文件描述符

#include <unistd.h>

int dup(int oldfd);

例子:
int ret = dup(3); 

值==3的文件描述符指向一个文件a.txt,返回值是从空闲的文件描述符表中找到的最小的一个, 这时候 4 也指向a.txt

  • dup2 重定向文件描述符

#include <unistd.h>

int dup2(int oldfd, int newfd);

dup2把newfd和newfd指向的文件都close,然后把newfd指向oldfd所指向的文件。

例如:oldfd指向 a.txt, newfd 指向 b.txt,函数调用成功之后: newfd和b.txt做close, newfd指向了a.txt

 oldfd必须是一个有效的文件描述符  , oldfd和newfd是相同的值, 等于什么也没做

 

  • fcntl

常用用法:

#include <unistd.h>
#include <fcntl.h>

int ret = fcntl(fd, F_DUPFD);
 - 参数: F_DUPFD, 复制文件描述符, 复制的是第一个参数: fd, 会得到一个新的文件描述符: 返回值ret   (此用法 == dup)
 - 参数: F_GETFL -> 获取文件的flags属性
 - 参数: F_SETFL -> 设置文件的flags属性

例子:

// 设置/获取文件open时的状态标志 -> flags

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

 - 必选项: O_RDONLY,  O_WRONLY,  or  O_RDWR, 这些属性无法修改
 	- 在open的时候设置了 O_RDONLY, 不能通过fcntl 设置 -> O_RDWR
 - 可以通过fcntl修改flags中的可选项
 	- O_APPEND    数据追加 
 		O_WRONLY | O_APPEND   写追加 
 	- O_NONBLOCK  设置非阻塞

修改文件flags属性测试代码 :

#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    // 打开一个文件
    int fd = open("1.txt", O_WRONLY);
    if(fd == -1)
    {
        perror("open");
        return -1;
    }

    // 追对文件的操作属性,先得到原来设置的属性
    int flag = fcntl(fd, F_GETFL); 

    // 属性添加
    flag |= O_APPEND;

    // 设置属性到文件描述符中
    fcntl(fd, F_SETFL, flag);

    // 写文件
    char* p = "你好, 世界....";
    write(fd, p, strlen(p));
    close(fd);

    return 0;
}

 

四、Linux系统IO函数

 

  • errno -> 属于Linux系统函数库, 库里边的一个全局变量, 记录的是错误号
#include <stdio.h>
void perror(const char *s);           - s参数: 用户描述, 比如:hello

perror打印的是errno对应的错误描述, 实际输出: hello: xxxxx(实际的错误描述)

 

  •  open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
  // open函数
  // 打开一个已经存在文件
  int open(const char *pathname, int flags);
  	参数:
          - pathname: 要打开的文件路径
          - flags: 对文件的操作权限设置
              - O_RDONLY,  O_WRONLY,  O_RDWR 这三个设置是互斥的
       返回值: 
           代码打开一个文件意味着获得了这个文件的访问句柄
  // 使用open创建一个新文件
  int open(const char *pathname, int flags, mode_t mode);
  	参数: 
    	- pathname: 要打开的文件路径
          - flags: 对文件的操作权限设置
              - 必选项: O_RDONLY,  O_WRONLY,  O_RDWR 这三个设置是互斥的
              - 可选项: O_CREAT -> 文件不存在, 创建新文件
          - mode: 八进制的数, 表示用户对创建出的新文件的操作权限, 比如: 0775, mode & ~umask
          		0777	-> 111111111
          		0775    -> 111111101
          	&	
                  0775       111111101
         按位与: 0和任何数都为0

 

  • close
  #include <unistd.h>
  int close(int fd);

 

  • read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  	参数: 
  		- fd: open得到的, 通过这个fd操作某一个文件
  		- buf: 缓冲区, 存储读到的数据, 数组的地址
  		- count: buf的大小
  	返回值: 
  		- 成功: 
  			>0: 返回实际读到的字节数
  			==0: 文件已经读完了
  		- 失败: -1

 

  • write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  	参数: 
  		- fd: open得到的, 通过这个fd操作某一个文件
  		- buf: 要往磁盘写入的数据
  		- count: 要写的数据的实际大小
  	返回值:
  		成功: 实际写入的字节数
  		失败: -1

 

  • lseek
#include <sys/types.h>
#include <unistd.h>

  off_t lseek(int fd, off_t offset, int whence);
  	参数: 
  		- fd: open得到的, 通过这个fd操作某一个文件
  		- offset: 偏移量
  		- whence: 
  			SEEK_SET
                设置文件指针的偏移量
         		SEEK_CUR
                设置偏移量: 当前位置 + 第二个参数的值
         		SEEK_END
                设置偏移量: 文件大小 + 第二个参数的值
  int fseek(FILE *stream, long offset, int whence);
  
  
  // 1. 移动文件指针到文件头
  lseek(fd, 0, SEEK_SET);

  // 2. 获取当前文件指针的位置
  lseek(fd, 0, SEEK_CUR);

  // 3. 获取文件长度
  lseek(fd, 0, SEEK_END);

  // 4. 拓展文件长度, 当前文件10b, 110b, 增加的字节的0
  lseek(fd, 100, SEEK_END);
  // 要进行一次写操作
  write(fd, " ", 1);

 

五、Linux其他系统函数

  • stat / lstat      根据文件名字,获取文件对应属性。
 struct stat {
      dev_t          st_dev;        	//文件的设备编号
      ino_t           st_ino;        	//节点
      mode_t      st_mode;      	//文件的类型和存取的权限
      nlink_t        st_nlink;     	//连到该文件的硬连接数目,刚建立的文件值为1
      uid_t           st_uid;       	//用户ID
      gid_t           st_gid;       	//组ID
      dev_t          st_rdev;      	//(设备类型)若此文件为设备文件,则为其设备编号
      off_t            st_size;      	//文件字节数(文件大小)
      blksize_t     st_blksize;   	//块大小(文件系统的I/O 缓冲区大小)
      blkcnt_t      st_blocks;    	//块数
      time_t         st_atime;     	//最后一次访问时间
      time_t         st_mtime;     	//最后一次修改时间
      time_t         st_ctime;     	//最后一次改变时间(指属性)
  };
  关于变量 st_mode: 
  	- st_mode -- 16位整数
  		○ 0-2 bit -- 其他人权限
  			- S_IROTH    00004  读权限
  			- S_IWOTH    00002  写权限
  			- S_IXOTH    00001  执行权限
  			- S_IRWXO    00007  掩码, 过滤 st_mode中除其他人权限以外的信息
  		○ 3-5 bit -- 所属组权限
  			- S_IRGRP    00040  读权限
  			- S_IWGRP    00020  写权限
  			- S_IXGRP    00010  执行权限
  			- S_IRWXG    00070  掩码, 过滤 st_mode中除所属组权限以外的信息
  		○ 6-8 bit -- 文件所有者权限
  			- S_IRUSR    00400    读权限
  			- S_IWUSR    00200    写权限
  			- S_IXUSR    00100    执行权限
  			- S_IRWXU    00700    掩码, 过滤 st_mode中除文件所有者权限以外的信息
  		○ 12-15 bit -- 文件类型
  			- S_IFSOCK   0140000 套接字
  			- S_IFLNK    0120000 符号链接(软链接)
  			- S_IFREG    0100000 普通文件
  			- S_IFBLK    0060000 块设备
  			- S_IFDIR    0040000 目录
  			- S_IFCHR    0020000 字符设备
  			- S_IFIFO    0010000 管道
  			- S_IFMT     0170000 掩码,过滤 st_mode中除文件类型以外的信息
  		(st_mode & S_IFMT) ==  S_IFREG
  


头文件:
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>

函数说明:
  int stat(const char *pathname, struct stat *buf);
  	参数: 
  		- pathname: 操作的文件的路径
  		- buf: 结构体变量, 传出


例子:
  struct stat st;
  stat("hello.txt", &st); 

stat是一个int类型数据,一个int数据可以表示32位2进制数,具体如下:

 

  • opendir  目录遍历函数
DIR *opendir(const char *name);
  	参数: 
  		- name: 要打开的目录
  	返回值: DIR * 
  struct dirent
  {
      ino_t d_ino;               // 此目录进入点的inode
      ff_t d_off;                // 目录文件开头至此目录进入点的位移
      signed short int d_reclen; // d_name 的长度, 不包含NULL 字符
      unsigned char d_type;      // d_name 所指的文件类型 
      har d_name[256];	       // 文件名
  };
  d_type
  	DT_BLK 	- 块设备
  	DT_CHR 	- 字符设备
  	DT_DIR 	- 目录
  	DT_LNK 	- 软连接
  	DT_FIFO - 管道
  	DT_REG 	- 普通文件
  	DT_SOCK - 套接字
  	DT_UNKNOWN - 未知
  struct dirent *readdir(DIR *dirp);
  	- 返回一个结构体, 这个对应一个文件
  int closedir(DIR *dirp);

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章