Linux系统IO

系统IO

open read write close lseek dup dup2 sprintf sscanf

标准IO

fopen fread fwrite fclose fseek ftell rewind

fprintf fscanf fgets gets getchar putchar getc putc

目录操作

opendir readdir closedir

系统IO和标准IO的区别

描述方式

系统IO采用文件描述符来表示文件

标准IO采用FILE*来表示文件

定义不同

系统IO 0 1 2

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

标准IO stdin stdout stderr

1
2
3
FILE *stdin     标准输入
FILE *stdout 标准输出
FILE *stderr 标准错误输出

缓冲区

系统IO没有缓冲区,如果是打开读写硬件传感器的驱动,一般都是使用系统IO
open write

标准IO带缓冲区,标准IO读写普通文件的速度效率要比系统IO要高
fopen fwrite

linux中的文件类型

  • 普通文件
  • 目录文件:文件夹
  • 字符设备:驱动编程
  • 块设备:驱动编程
  • 软链接(符号链接)
  • 套接字:网络编程
  • 管道文件: 系统编程

文件权限

权限值

用二进制来表示读写执行属性,将二进制转换为十进制来操作

权限掩码

权限掩码是一组用于配合linux系统,对新建的文件与文件夹进行权限分配的八进制数

查看掩码值

umask

0002 //第一个0表示是八进制,后面三个数组才是掩码值

修改掩码值

umask 新的掩码值

比如:umask 0022 //掩码022表示要去掉同组用户的写权限,其他用户的写权限

系统IO:文件操作

通过文件描述符来对文件进行操作

文件描述符是一个int类型的数据,是一个定义在

/usr/src/linux-headers-XXXX/include/linux/sched.h

中的结构体struct task_struct的引索值,表示的是这个结构体中保存的文件的引索值,这个结构体是用来保存当前程序运行时候产生的状态信息的,包含有当前程序打开的所有文件的属性信息,结构体数组的起始位置(该结构体数组每个成员分别存储你打开的每一个文件的属性信息),当前程序占用的内存空间大小,当前程序栈的使用情况,当前程序堆的使用情况等信息

文件描述符的分配规则

  • 0 1 2已经默认被键盘(STDIN),液晶屏(STDOUT,STDERROROUT)占用了,只能从3开始分配
  • linux系统总是把目前没有被占用的最小的文件描述符分配给你

文件的偏移

类似于window上的光标位置,当你打开文件默认偏移位置是在文件的开头

当你调用read/write读写文件的时候,文件偏移会自动变动

打开文件

1
2
3
4
5
6
7
8
9
10
11
12
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
* @brief 打开文件
*
* @param pathname 文件路径+文件名
* @param flags 打开方式
* @param mode 可选参数 创建文件时设置权限,会经过umask调整 结果为mode&(~umask)
* @return int 成功 文件描述符 | 失败 -1
*/
int open(const char *pathname, int flags,{mode_t mode});

flags

  • O_RDONLY 只读
  • O_WRONLY 只写
  • O_RDWR 可读写
  • O_APPEND 以追加的方式打开文件
  • O_CREAT 新建文件
  • O_EXCL 跟O_CREAT配合使用,表示如果文件存在就失败退出,不新建,不存在就新建
  • O_TRUNC 跟O_CREAT配合使用,表示如果文件存在就清空覆盖掉原来的文件

读取文件

1
2
3
4
5
6
7
8
9
10
#include <unistd.h>
/*
* @brief 读取文件
*
* @param fd 文件描述符
* @param buf 读取后存放内容的位置
* @param count 读取字节数量
* @return ssize_t 成功读取到的字节数 如果文件读取完毕后再次调用read,则返回0个字节 | 失败 -1
*/
ssize_t read(int fd, void *buf, size_t count);

写入文件

1
2
3
4
5
6
7
8
9
/*
* @brief 写入文件
*
* @param fd 文件描述符
* @param buf 要写入的内容
* @param count 要写入的字节数量
* @return ssize_t 要写入的字节数量
*/
ssize_t write(int fd, const void *buf, size_t count);
  • 若count值比buf的长度大,则会从调用底下的IO缓冲区中获取内容填充进去fd中,这个结果是不可控的,因此coun值大小必须严格小于等于buf的长度

文件关闭

1
2
3
4
5
6
7
/*
* @brief 关闭文件
*
* @param fd 要关闭的文件描述符
* @return int 成功 0 | 失败 -1
*/
int close(int fd);

获取文件属性

1
2
3
4
5
6
7
8
/*
* @brief 获取文件属性
*
* @param pathname 文件路径+文件名
* @param buf 存放文件信息的结构体stat的指针
* @return int 成功 0 | 失败 -1
*/
int stat(const char *pathname, struct stat *buf);

获取文件大小

结构体stat中的字段off_t st_size; 存放的是stat对应文件的大小

获取文件类型

结构体stat中的字段mode_t st_mode 存放的是stat对应文件的类型和权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 文件类型相关的宏定义
S_IFSOCK 0140000 套接字
S_IFLNK 0120000 软链接
S_IFREG 0100000 普通文件
S_IFBLK 0060000 块设备
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符设备
S_IFIFO 0010000 管道
// 文件类型相关的宏函数
S_ISREG(m) 普通文件
S_ISDIR(m) 目录
S_ISCHR(m) 字符设备
S_ISBLK(m) 块设备
S_ISFIFO(m) 管道
S_ISLNK(m) 软链接
S_ISSOCK(m) 套接字

可以使用if ( filestat.st_mode & S_IFREG ) 等来判断该文件是否属于对应的文件类型

可以使用if ( S_ISLNK ( filestat.st_mode ) ) 等来判断该文件是否属于对应的文件类型

stat()无法判断软链接类型,可以使用lstat()或者宏函数S_ISLNK(m)来判断

获取文件权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 文件权限相关的宏定义
S_IRWXU //当前用户可读,可写,可执行
S_IRUSR //当前用户可读
S_IWUSR //当前用户可写
S_IXUSR //当前用户可执行
S_IRWXG //同组用户可读,可写,可执行
S_IRGRP //同组用户可读
S_IWGRP //同组用户可写
S_IXGRP //同组用户可执行
S_IRWXO //其他用户可读,可写,可执行
S_IROTH //其他用户可读
S_IWOTH //其他用户可写
S_IXOTH //其他用户可执行

修改文件权限

1
2
3
4
5
6
7
8
/*
* @brief 修改文件权限
*
* @param pathname 文件路径+文件名
* @param mode 要修改的文件权限 八进制数 不需要考虑掩码umask
* @return int 成功 0 | 失败 -1
*/
int chmod(const char *pathname, mode_t mode);

文件存在性

1
2
3
4
5
6
7
8
/*
* @brief 修改文件权限
*
* @param pathname 文件路径+文件名
* @param mode 要修改的文件权限 八进制数 不需要考虑掩码umask
* @return int 文件存在 0 | 不存在 -1
*/
int access(const char *pathname, int mode);

mode

  • R_OK 判断文件是否可读
  • W_OK 判断文件是否可写
  • X_OK 判断文件是否可执行
  • F_OK 判断文件是否存在

文件读写偏移值

1
2
3
4
5
6
7
8
/**
* @brief 修改文件权限
*
* @param fd 文件描述符
* @param offse 读写偏移量(字节)
* @return int 成功 返回当前偏移位置距离文件开头的字节数 | 失败 -1
*/
off_t lseek(int fd, off_t offset, int whence);

whence 初始偏移点

  • SEEK_SET 起始位置
  • SEEK_CUR 当前位置,动态变化
  • SEEK_END 末尾位置

可以使用lseek(fd,0,SEEK_END);求文件的大小

异常捕获

1
2
3
4
5
6
7
#include <errno.h>
/**
* @brief 在用户输出的提示信息后输出 errno 宏中错误码对应的错误信息
*
* @param s 要输出的提示信息
*/
void perror(const char *s);

linux的errno.h中有定义全局变量 int errno用来记录保存当前的错误码,perror依据errno中的错误码找到错误的具体原因

文件重定向

1
2
3
4
5
6
7
/**
* @brief 给原来的文件描述符分配一个新的文件描述符
*
* @param oldfd 原本的文件描述符
* @return int 文件描述符 | 失败 -1
*/
int dup(int oldfd);
1
2
3
4
5
6
7
8
/**
* @brief 指定分配一个新的文件描述符
*
* @param oldfd 原本的文件描述符
* @param newfd 指定分配的新文件描述符
* @return int 指定的文件描述符 | 失败 -1
*/
int dup2(int oldfd, int newfd);
  • dup2可以把0 1 2重定向出来
  • dup是系统自动分配一个新的文件描述符

标准IO : 文件操作

打开文件

1
2
3
4
5
6
7
8
/**
* @brief 打开文件
*
* @param pathname 要打开的文件路径名
* @param mode 打开方式
* @return FILE * 返回FILE指针 | 失败 NULL
*/
FILE *fopen(const char *pathname, const char *mode);

mode

  • r 只读
  • r+ 可读写
  • w 可写
  • w+可读写,如果文件不存在,就新建,存在则清空覆盖
  • a可写 追加
  • a+ 可读写 追加

读取文件

1
2
3
4
5
6
7
8
9
10
/**
* @brief 读取文件
*
* @param ptr 存放读取的内容的指针
* @param size 读取的数据块大小(字节)
* @param nmemb 读取的数据块数量
* @param stream 要打开的FILE指针
* @return size_t 读取到的完整的数据块的个数 | 失败 -1
*/
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

写入文件

1
2
3
4
5
6
7
8
9
10
/**
* @brief 写入文件
*
* @param ptr 存放写入的内容的指针
* @param size 写入的数据块大小(字节)
* @param nmemb 写入的数据块数量
* @param stream 要打开的FILE指针
* @return size_t nmemb
*/
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

关闭文件

1
2
3
4
5
6
7
/**
* @brief 关闭文件
*
* @param * stream 要关闭的FILE指针
* @return int
*/
int fclose(FILE *stream);

文件偏移

1
2
3
4
5
6
7
8
9
/**
* @brief 文件偏移
*
* @param stream 要偏移的FILE指针
* @param offset 偏移值
* @param whence 初始偏移点
* @return int 成功 0 | 失败 -1
*/
int fseek(FILE *stream, long offset, int whence);

whence 初始偏移点

  • SEEK_SET 起始位置
  • SEEK_CUR 当前位置,动态变化
  • SEEK_END 末尾位置
1
2
3
4
5
6
7
/**
* @brief 返回当前偏移值
*
* @param stream 要计算的FILE指针
* @return long 当前位置距离起始位置的字节数
*/
long ftell(FILE *stream);
1
2
3
4
5
6
7
/**
* @brief 跳转偏移到文件起始位置
*
* @param * stream 要偏移的FILE指针
*/
void rewind(FILE *stream);
//等价于 fseek(filep,0,SEEK_SET);

标准IO : 缓冲区操作

fgets

1
2
3
4
5
6
7
8
9
/**
* @brief 按行读取数据,一次最多读取一行(遇到回车结束读取)
*
* @param * s 读取到的一行数据后的存储地址
* @param * size 读取多少字节的数据
* @param * stream 文件
* @return char 成功 返回读取的一行数据的首地址 | 失败 NULL
*/
char *fgets(char *s, int size, FILE *stream);
1
2
3
4
// 假如一行只有10个字符
fgets(buf,5,myfile); // 只能读取4个有效字符,最后一个fgets会帮你填充\0
fgets(buf,11,myfile); // 前面10个是有效字符,第11个是\0
fgets(buf,12,myfile); // 前面11个是有效字符(会把回车\n读取,windows上的回车是\r\n),第12个是\0

fputs

1
2
3
4
5
6
7
8
/**
* @brief 往一个文件中写入字符串
*
* @param * s 要写入的字符串
* @param * stream 文件
* @return int 成功 >0 | 失败 EOF
*/
int fputs(const char *s, FILE *stream);

fgetc

1
2
3
4
5
6
7
/**
* @brief 按字符读取数据,一次只能读取一个字节的数据
*
* @param * stream 文件
* @return int 成功 读取到的那个字符的ASCII码值 | 失败 -1
*/
int fgetc(FILE *stream);

fputc

1
2
3
4
5
6
7
8
/**
* @brief 按字符写入数据
*
* @param * c 要写入的字符
* @param * stream 文件
* @return int
*/
int fputc(int c, FILE *stream);

gets

1
2
3
4
5
6
7
/**
* @brief 读取键盘输入的字符串
*
* @param * s 要存放字符串的地址
* @return char
*/
char *gets(char *s);

puts

1
2
3
4
5
6
7
/**
* @brief 把字符串在标准输出(液晶屏)上打印出来
*
* @param * s 要输出的字符串
* @return int
*/
int puts(const char *s)

getc

1
2
3
4
5
6
7
/**
* @brief 按字符读取数据,一次只能读取一个字节的数据
*
* @param * stream 要读取字符的文件流
* @return int 成功 读取到的那个字符的ASCII码值 | 失败 -1
*/
int getc(FILE *stream);

putc

1
2
3
4
5
6
7
/**
* @brief 把c这个字符写入到stream表示的文件中
*
* @param * s 要输出的字符串
* @return int
*/
int putc(int c, FILE *stream);

putchar

1
2
3
4
5
6
7
/**
* @brief 把c代表的字符在液晶屏上打印出来
*
* @param * c 要输出的字符
* @return int
*/
int putchar(int c);

getchar

1
2
3
4
5
6
/**
* @brief 从IO缓冲区读取一个字符
*
* @return int 成功 字符的ASCII码 | 失败 -1
*/
int getchar(void);

feof

1
2
3
4
5
6
7
/**
* @brief 判断是否到达文件末尾
*
* @param * c 要判断的文件流
* @return int 达到文件末尾 1 | 未到达 0
*/
int feof(FILE *stream);

系统IO:字符串操作

字符串拼接

1
2
3
4
5
6
7
8
9
/**
* @brief 字符串拼接
*
* @param str 存放拼接后的结果
* @param format 格式控制流
* @param ... 可变参数 数据集
* @return int
*/
int sprintf(char *str, const char *format, ...);

字符串拆分

1
2
3
4
5
6
7
8
9
/**
* @brief 字符串拆分
*
* @param str 要拆分的字符串
* @param format 格式控制流
* @param ... 可变参数 数据集
* @return int
*/
int sscanf(const char *str, const char *format, ...);

字符串切割

1
2
3
4
5
6
7
8
/**
* @brief 字符串切割
*
* @param str 要切割的字符串
* @param delim 切割标志
* @return char* 切割得到的字符串 | 失败 NULL
*/
char *strtok(char *str, const char *delim);

标准IO:字符串操作

fprintf

1
2
3
4
5
6
7
8
/**
* @brief 按照指定格式,把数据合并写入到stream代表的文件中
*
* @param stream 要写入的文件流
* @param format 格式控制串 ...
* @return int
*/
int fprintf(FILE *stream, const char *format, ...);

fscanf

1
2
3
4
5
6
7
8
/**
* @brief 按照指定格式,把数据读取出来
*
* @param stream 要读取的文件流
* @param format 格式控制串 ...
* @return int
*/
int fscanf(FILE *stream, const char *format, ...);

缓冲区

主动刷新缓冲区的情况

正常情况下标准IO是带缓冲区:

  • 情况一:缓冲区满了,自动刷新
  • 情况二:缓冲区没有满,主函数return退出的时候也会自动帮助你刷新缓冲区
  • 情况三:fclose关闭文件的时候也会刷新缓冲区
  • 情况四:调用fflush主动刷新缓冲区 int fflush(FILE *stream);

目录操作

要先引入 #include <dirent.h>

打开目录

1
2
3
4
5
6
7
/**
* @brief 打开目录
*
* @param name 目录的路径名
* @return 成功 返回DIR指针表示你打开的那个目录 | 失败 NULL
*/
DIR *opendir(const char *name);

读取目录

1
2
3
4
5
6
7
/**
* @brief 读取目录
*
* @param name 目录
* @return 成功 返回目录项结构体
*/
struct dirent *readdir(DIR *dirp);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct dirent {
ino_t d_ino; //节点编号
off_t d_off; //文件偏移位置
unsigned short d_reclen;
unsigned char d_type; //文件类型
/*
DT_BLK 块设备
DT_CHR 字符设备
DT_DIR 目录
DT_FIFO 管道
DT_LNK 软链接、符号链接
DT_REG 普通文件
DT_SOCK 套接字
DT_UNKNOWN 无法识别的文件类型
*/
char d_name[256]; //文件名字
};

新建目录

1
2
3
4
5
6
7
8
9
/**
* @brief 新建目录
*
* @param name 目录
* @param name 权限 权限0777 0666 表面上你写的是0777,
* 但是实际的权限计算公式是: 0777&(~umask)
* @return 成功 0 | 失败 -1
*/
int mkdir(const char *pathname, mode_t mode);

关闭目录

1
2
3
4
5
6
7
/**
* @brief 关闭目录
*
* @param *dirp 目录
* @return 成功 0 | 失败 -1
*/
int closedir(DIR *dirp);

系统IO:内存映射

延时函数

1秒=1000毫秒
1毫秒=1000微秒

  • 以秒为单位

unsigned int sleep(unsigned int seconds);

  • 以微秒为单位

int usleep(useconds_t usec);

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2022 Elimos
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信