目录
sync函数:页缓存和回写,强制把修改过的页缓存区数据写入磁盘
直接进行文件系统的底层操作(需要程序员熟悉文件系统的结构,并编写大量的代码完成)。
调用shell程序实现,c语言提供了访问shell程序的接口,但shell的返回信息不便于在程序中进行分析。
借助系统调用实现(如系统IO等)。
Linux内核:屏蔽硬件区别,把所有的硬件设备抽象成文件,提供统一的接口给用户使用,是一系列设备的驱动程序。
文件操作必须通过物理存储设备的驱动程序访问驱动器,如硬盘、光盘驱动器的驱动程序,这些驱动程序存放在Linux内核。
系统调用函数是Linux内核中实现的,是应用程序和linux内核交互的接口。
在linux中,大部分的头文件在系统的“/usr/include”目录下可以找到,它是系统自动的GCC编译器默认的头文件目录。在预定义语句中包含系统调用函数库需要在头文件前加上相对路径sys/。可以用locate来定位头文件的位置,比如locate sys/stat.h。
系统调用并非是ANSI C标准,所有不同的操作系统或不同的linux内核版本的系统调用函数可能不同。
Linux提供的系统调用包含以下内容:
进程控制:如fork、clone、exit、setpriority等创建、克隆、中止、设置进程优先级的操作。
文件系统控制:如open、read、write等对文件的打开、读取、写入操作。
系统控制:如reboot、stime、init_module等重启、调整系统时间、初始化模块的系统操作。
内存管理:如mlock、mremap等内存也上锁、重映射虚拟内存操作。
网络管理:如sethostname、getthostname设置或获取本主机名操作。
socket控制:如socket、bind、send等进行TCP、UDP的网络通讯操作。
用户管理:如setid、getid等设置或获取用户ID的操作。
进程间通信:包括信号量、管道、共享内存等操作。
虚拟文件系统:抽象层,对文件的访问实际上是对抽象层的访问。
抽象对象:封装了底层读写细节,使用c语言的多态来实现具体文件系统的接口。
普通文件系统:ext4、fat32、ubifs
特殊文件系统
进程文件系统:procfs,挂载在/proc,存放进程相关信息,任务管理器。
设备文件系统:devfs,挂载在/dev。存放硬件操作接口。
阻塞模式、非阻塞模式、IO多路复用、异步IO、信号驱动IO。
非缓冲文件操作,适合于小规模文件的读写和对实时性要求很高的设备的数据通信,这类操作是系统调用提供的。比如:调制解调器、连接于串口的工业设备。read()函数和write()函数。
缓冲文件操作,适合于大规模非实时性数据的处理,这类操作是标准输入输出库提供的。
stdio.h:C标准输入输出头文件,常使用printf函数。
stdlib.h:C标准库头文件,常用malloc、free等函数。
sys/stat.h:包含了关于文件权限定义,如S_IRWXU、S_IWUSR以及函数fstat用于查询文件状态。涉及系统调用文件相关的操作,通常都需要用到sys/stat.h文件。
unistd.h:UNIX C标准库头文件,unix、linux系列的操作系统相关的C库,定义了unix类系统POSIX标准的符合常量头文件,比如Linux标准的输入文件描述符(STDIN)、标准输出文件描述符(STDOUT)和read、write等系统调用的声明。
fcntl.h:unix标准中通用的头文件,其中包含的相关函数有open、fcntl、close等操作。
sys/types.h:包含Unix/Linux系统的数据类型的头文件,常用的有size_t、time_t、pid_t等类型。
文件描述符:小的非负整数,内核用以标识某一特定进程正在存访的文件。
当内核打开一个现存文件或创建一个新文件时,它会返回一个文件描述符给该进程,这样该进程就能通过内核访问目标文件。
int fd; //定义文件描述符
fd = open(filename, flags, mode); //open(文件名,模式,权限(可选))
lseek(fd, offset, whence); //lseek(文件描述符,读写位置的偏移量,文件读写位置的基准值)
write(fd, buf, write_len);
read(fd, buf, read_len);
close(fd);
主模式(互斥,只选其一) | 描述 | 副模式(兼容,可多选) | 描述 |
O_RDONLY | 只读模式 | O_CREAT | 当文件不存在,需要去创建文件 |
O_WRONLY | 只写模式 | O_APPEND | 追加模式 |
O_RDWR | 读写模式 | O_DIRECT | 直接IO模式,读写数据跳过页缓存区,直接在磁盘操作 |
O_SYNC | 同步模式,不需要手动添加sync函数 | ||
O_NOBLOCK | 非阻塞模式 |
//所需头文件
#include//提供mode_t类型
#include//提供open()函数的符号
#include//提供open()函数
//函数原型
int open(const char* pathname,int flags); //当文件存在时
int open(const char* pathname,int flags,int perms); //当文件不存在时
//返回值
成功:文件描述符
失败:-1
//所需头文件
#include//提供close()函数
//函数原型
int close(int fd);
//返回值
成功:0
失败:-1
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- int fd;
- fd = open("./a.txt", O_RDONLY|O_CREAT, 0666);
- if(fd < 0)
- {
- printf("open error!\r\n");
- }
- printf("fd:%d\r\n", fd);
- close(fd);
-
- return 0;
- }
//所需头文件
#include
//函数原型
ssize_t read(int fd, void *buff, size_t count);
//返回值
成功:
count:成功读取全部字节
0~count:
剩余文件长度小于count
读取期间被异步信号打断
失败:-1,读取错误
//所需头文件
#include
//函数原型
ssize_t write(int fd,void *buff,size_t count);
//返回值
成功:
count:成功写入全部字节
0~count:写入期间被异步信号打断
失败:-1,写入错误
- #include
- #include
- #include
- #include
- #include
-
- int main(int argc,char** argv)
- {
- int fd1, fd2;
- char buf[512];
- int read_size;
-
- if(argc != 3)
- {
- printf("param error!\r\n");
- return -1;
- }
-
- fd1 = open(argv[1], O_RDONLY);
- fd2 = open(argv[2], O_WRONLY | O_CREAT, 0666);
- if( fd1 < 0 || fd2 < 0 )
- {
- printf("open error!\r\n");
- return -1;
- }
-
- while(1)
- {
- read_size = read(fd1, buf, 512);
- if(read_size == 0)
- break;
- write(fd2, buf, read_size);
- }
-
- close(fd1);
- close(fd2);
-
- return 0;
- }
whence | 描述 |
SEEK_SET | 基准点为文件开头 |
SEEK_CUR | 基准点为当前位置 |
SEEK_END | 基准点为文件末尾 |
//所需头文件
#include
//函数原型
off_t lseek(int fd,off_t offset,int whence);
//返回值
成功:文件偏移位置值
失败:-1
//所需头文件
#include
//函数原型
void sync(void);
//返回值
无
- #include
- #include
- #include
- #include
- #include
-
- int main(int argc, char** argv)
- {
- int fd=open("lseek.txt", O_RDWR | O_CREAT, 0666);
-
- write(fd, "123", 3);
- lseek(fd, 3, SEEK_CUR);
- write(fd, "abc", 3);
- sync();
- close(fd);
-
- return 0;
- }
//所需头文件
#include
#include
#include
//函数原型
int fstat(int fd, struct stat *statbuf); //返回一个已打开文件的状态和属性信息
int lstat(const char *pathname, struct stat *statbuf); //返回一个未打开的符号链接文件本身的信息
int stat(const char *pathname, struct stat *statbuf); //返回一个未打开的符号链接文件指向文件的信息
struct stat {
dev_t st_dev; //包含文件的设备ID
ino_t st_ino; //Inode号
mode_t st_mode; //文件类型和模式
nlink_t st_nlink; //硬链接数量
uid_t st_uid; //所有者的用户ID
gid_t st_gid; //所有者的群组ID
dev_t st_rdev; //设备ID(如果是特殊文件)
off_t st_size; //总大小,以字节为单位
blksize_t st_blksize; //IO文件系统的块大小
blkcnt_t st_blocks; //申请的块数(一块等于512B)
//从Linux 2.6开始,内核支持以下时间戳字段的纳秒精度。有关Linux 2.6之前的详细信息,请参见NOTES
struct timespec st_atim; //最后访问时间
struct timespec st_mtim; //最后修改时间
struct timespec st_ctim; //上次状态更改时间
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec#define st_ctime st_ctim.tv_sec
};
//返回值
成功:0
失败:-1,错误置于errno
chmod(path, 权限); //属性操作
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main(int argc,char** argv)
- {
- struct stat buf;
- int fd = open("stat.txt", O_RDWR|O_CREAT, 0111);
-
- fstat(fd, &buf);
- if(buf.st_mode & S_IRUSR)
- puts("所有者拥有读权限");
- if(buf.st_mode & S_IRGRP)
- puts("群组拥有读权限");
- close(fd);
-
- chmod("stat.txt", 0777);
- stat("stat.txt", &buf);
- if(buf.st_mode & S_IWUSR)
- puts("所有者拥有写权限");
- if(buf.st_mode & S_IWGRP)
- puts("群组拥有写权限");
-
- return 0;
- }
//所需头文件
#include
mkdir(path, 权限); //新建目录,返回0为成功,返回-1为失败
getcwd(char *buf, size_t size); //获取当前工作目录,返回当前目录的字符串长度,当大于size时返回NULL
chdir(path); //相当于cd命令,改变执行程序的工作目录
rmdir(path); //删除目录,该函数必须在该目录下没有子目录或文件的情况才能运行
unlink(path); //删除文件
扫描子目录函数封装在dirent.h文件,使用DIR结构体,这个结构的指针所指向的内存空间被称为子目录流。
struct dirent {
ino_t d_ino; //Inode号
off_t d_off; //该文件相对于文件夹的偏移量
unsigned short d_reclen; //d_name的长度
unsigned char d_type; //文件类型, 例如管道, Socket , Block等;并非所有文件系统类型都支持
char d_name[256]; //文件名
};
函数名 | 描述 |
DIR *opendir(const char *name) | 打开路径并建立子目录流,返回子目录流指针 |
struct dirent *readdir(DIR *dirp) | 函数返回一个指针,指针指向的结构体保存着子目录流dirp中下一个目录数据项的有关资料。 后续的readdir调用将返回后续的目录数据。如果错误或到达子目录尾,将返回NULL值。 |
long int telldir(DIR *dirp) | 函数返回值里记录着子目录流里的当前位置 |
void seekdir(DIR *dirp, long int loc) | 对dirp指定的子目录流中的目录数据项的指针进行设置。 loc的值用于设置指针位置,它应该通过前一个telldir函数调用获得。 |
int *closedir(DIR *dirp) | 关闭子目录流,返回关闭操作结果 |
可遍历子目录下所有文件的函数,scan_dir.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int scan_dir(char *dir, int depth)
- {
- DIR *dp;
- struct dirent *entry;
- struct stat statbuf;
-
- if( (dp = opendir(dir)) == NULL )
- {
- puts("无法打开该目录");
- return -1;
- }
-
- chdir(dir);
- //readdir是逐一读取文件(目录也属于文件的一种),会读取.和..
- while( (entry = readdir(dp)) != NULL )
- {
- /* 读取文件属性 */
- lstat(entry->d_name, &statbuf);
- if(statbuf.st_mode & S_IFDIR) //目录文件
- {
- if( strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0 )
- continue;
- //printf,%*s,depth指定缩进宽度(目前为0),“”表示缩进的内容是空格
- printf("%*s%s/\n", depth, "", entry->d_name);
- scan_dir(entry->d_name, depth + 4);
- }else //普通文件
- {
- printf("%*s%s/\n", depth, "", entry->d_name);
- }
- }
-
- chdir("..");
- closedir(dp);
- }
使用readdir函数读取目录中的每一个文件(包括目录和文件,因为目录是一个特殊文件),并逐一进行处理。首先使用lstat函数获取文件属性,然后判断是否为目录文件。如果是目录文件,则进行递归调用scan_dir函数,同时将深度+4,以便缩进显示。如果是普通文件,则直接输出文件名。
scan_dir.h
- #ifndef __MP3_H
- #define __MP3_H
-
- void scan_dir(char *dir, int depth);
-
- #endif
main.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- puts("扫描该工程目录:");
- scan_dir("/home/couvrir/桌面/make_testfile1", 0);
- puts("扫描结束!");
- return 0;
- }
在进行文件操作的过程中可能会因各种原因而失败,错误信息将以代码的形式保存在系统变量errno中。很多函数通过改变errno变量的值输出标准错误信息编码,这些错误信息保存在头文件errno.h中。
进行错误处理的函数:sterror()、perror()。perror函数内也调用了sterror函数,将标准错误信息字符串输出到终端,并为其增加一个说明。
因为很多函数都是以errno变量,当另一个函数操作完成后可能会改变该变量的值。所有要获得正确的错误信息,应该将取得errno变量值的语句或sterror()函数放在出错语句最近的位置。
比如:perror("文件操作");
终端显示可能是:
文件操作:No such file or directory
C标准库实现了一个IO缓存区。
常见标准IO函数(跟上述系统IO函数使用差不多):
fopen
fclose
fread
fwrite
fseek
fflush:强制把IO缓存区的数据写入到页缓存区