Appearance
文件IO
系统调用
操作系统提供给外界的程序编程接口(API)
C语言的IO函数
fopen、fclose、fseek、fgets、fputs、fread、fwrite......
r 只读、 r+读写
w只写并截断为0、 w+读写并截断为0
a追加只写、 a+追加读写
open
c
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 文件名
flags: 文件的打开方式, O_RDONLY、O_WRONLY、O_RDWR , O_APPEND、O_CREAT、O_EXCL(文件存在的时候返回一个错误)、 O_TRUNC(打开的时候把之前的数据丢弃)、 O_NONBLOCK(非阻塞)
modo: 文件的权限, 参数2有O_CREAT的时候才会使用这一个函数, 文件权限由open的mode参数和当前进程的umask掩码共同决定, 实际的是mode & ~umask
返回值: 是一个文件描述符, 错误的时候是-1
使用头文件: unistd.h
显示错误
strerror
Linux有一个全局变量errno, 使用头文件errno.h可以使用
还可以使用strerror获取这一个错误的描述, 头文件string.h
c
#include<stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
int fd;
fd = open("test.txt", O_CREAT| O_RDWR, 0666);
printf("fd = %d, errno = %d:%s \n", fd, errno, strerror(errno));
close(fd);
return 0;
}
perror
还可以使用perror(const char *);
这一个函数在打印这一个信息以后, 会根据errno的值进行显示实际的错误
read/write
c
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
文件, 保存的位置以及读取的大小
返回的时候返回实际读取的大小, 为0的时候已经到末尾了, On error, -1 is returned, and errno is set appropriately.
c
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
返回实际写入的数据的个数, 错误的话返回-1
在实际使用时候, 如果使用fget和read一次读取一个字符进行测试的话, 会发现fgetc的速度实际比read快, 原因是实际使用的时候, fgetc不是一个一个字节的进行的
可以使用命令
strace 可执行文件名
进行测试, 这一个命令会打印出使用的系统调用标准库里面有一个缓冲区, 这两个函数是无用户级缓冲
文件描述符
文件描述符是进程描述符里面的其中一项
打开一个文件以后, 实际的文件的描述符是一个struct file, 操作系统实际不会给你这一部分, 实际返回的是文件描述符
前三个文件给了标准的文件输入输出, 一个进程最大可以打开1024个文件, 实际打开的时候打开的是可以使用的最小的那一个
前三个使用的时候, 推荐使用这三个文件使用的宏定义
这一个文件描述符里面有
- 文件的偏移
- 文件的权限
- 文件的打开标志
- 文件的缓冲区
- 文件的操作函数
可以在/usr/src/linux-headers-5.4.0-152-generic/include/linux/fs.h这一个文件里面的struct file里面获取实际的实现
阻塞和非阻塞
在实际的读取一个文件的时候是不会阻塞的, 但是在读取文件以及网络的时候有可能会发生阻塞
可以直接使用read函数对STDIO_FILENO进行读取, 不需要对文件进行打开
c
#include <unistd.h>
#include <stdlib.h>
#include<stdio.h>
int main(void){
char buf[10];
int n;
n = read(STDIN_FILENO, buf, 10);
if(n < 0){
perror("read STDIN_FILENO");
exit(1);
}
write(STDOUT_FILENO, buf, n);
return 0;
}
这一个读取的时候会进行阻塞是文件的属性
可以再一次打开/dev/tty, 这时候打开的时候使用一个非阻塞, 如果读取的时候没有数据, 会返回一个-1, 同时会设置errno = EAGIN EWOULDBLOCK, 说明这个时候不是read失败, 而是read在读取一个设备文件的时候使用的是一个非阻塞, 并且这一个文件没有数据
fcntl改变一个文件的属性
int fcntl(int fd, int cmd, ... /* arg */ );
这里主要使用两个命令, F_GETFL, F_SETFL
这一个命令只能改变部分的文件标志
c
flag = fcntl(fd, F_GETFL);
flag |= O_NONBLOCK;
flag = fcntl(fd, F_SETFL, flag);
lseek文件偏移
和fseek类似
off_t lseek(int fd, off_t offset, int whence);
这一个可以超过文件的末尾, 文件可以被拓展, 实际拓展的时候需要一个写入
int fseek(FILE *stream, long int offset, int whence)
这一个超出文件的末尾返回0, 超出文件的头部返回-1, 常用的参数有SEEK_SET(文件的开头),SEEK_CUR(文件当前位置), SEEK_WND(文件的结尾)
可以使用这一个函数获取这一个文件的大小(从文件的头部到尾部, 返回这一个文件的偏移(相对于文件头部))
可以使用命令od -tcx查看文件的16进制模式, od -tcd查看文件的10进制模式
truncate拓展文件大小
int truncate(const char *path, off_t length);
这一个文件必须是已经存在的文件, 这一个命令不需要写入, 成功返回0
ioctl查看物理特性
c
#include <sys/ioctl.h>
int ioctl(int fd, ind cmd,...);
ioctl函数是用来操作特殊文件的底层设备参数。特别的,许多字符文件的操作特性是通过ioctl来控制的。
参数传递
传入参数:
1. 指针为参数
1. 通常有const关键字进行修饰
1. 使用指针进行读操作
c
char *strcpy(char *dest, const char *src);
src为传入函数, dest是传出参数
传出参数
- 指针为参数
- 函数调用之前, 指向的空间可以无意义, 但是是有效的
- 函数内部写操作
- 函数结束以后作为函数的返回值
传入传出参数
- 指针为函数参数
- 指向的空间有实际的意义
- 函数里面先进行读, 后进行写
- 结束以后是返回值
c
char *strtok_r(char *str, const char *delim, char **saveptr);
这一个函数实际的作用是进行字符串的分割
返回值: 实际获取的字符串
str是传入的字符串。需要注意的是 :第一次使用strtok_r之后,要把str置为NULL
delim指向依据分割的字符串。常见的空格“ ” 逗号“,”等
saveptr保存剩下待分割的字符串。
这一个函数里面的saveptr实际是一个传入传出参数