目录
一,Linux下的文件系统
UNIX下一切皆文件
1,Linux下文件主要分为一下几种
- 普通文件:例如保存在磁盘上的C文件,可执行文件,以及目录等,这种文件的特性是数据在存储设备上存放,内核提供了对数据的抽象访问,此种文件为一种字节流,访问接口完全独立于在磁盘上的存储数据。
- 字符设备文件:是一种能够像文件一样被访问的设备,例如控制台,串口等。
- 块设备文件:磁盘是此类设备文件的典型代表,与普通文件的区别是操作系统对数据的访问进行的重新的格式设计。
- socket文件:它是Linux中通过网络进行通信的方式,对网络的访问可以通过文件描述符的抽象实现,访问网络和访问普通文件相似
在Linux中用户对各种文件的操作是类似的,因为虚拟文件系统VFS提供了同一套API
2,文件系统的创建
2.1 系统分区情况
- fdisk -l 列出当前系统的磁盘情况
二,文件的通用操作方法
1,文件描述符
Linux系统中有三个已经分配的文件描述符
- 标准输入 0
- 标准输出 1
- 标准错误 2
2,打开创建open(),create()函数介绍
open() 打开一个已经存在的文件或创建一个新文件
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
- 包含头文件:sys/types.h,sys/stat.h,fcntl.h
- pathname:指定打开的文件路径
- flags :设置文件打开后允许的操作方式
- O_RDONLY: 只读
- O_WRONLY: 只写
- O_RDWR: 读写 在打开文件时必须指定以上的三种模式之一
- O_APPEND: 使每次对文件进行些写操作都追加到文件的尾端
- O_CREAT: 如果文件不存在则创建它,第三个参数mode需要指定文件权限
- O_EXCL: 查看文件是否存在。如果同时指定了O_CREAT,而文件已经存在,会返回错误
- O_TRUNC: 将文件长度截断为0
- 返回值:成功返回文件描述符,失败返回-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = -1;
char fileName[] = "test.txt";
fd = open(fileName, O_RDWR); //打开文件
if (-1 == fd)
{
printf("Open file %s failure!, fd:%d\n", fileName, fd);
}
else
{
printf("Open file %s success!, fd:%d\n", fileName, fd);
}
return 0;
}
create() 创建一个新文件
int creat(const char *pathname, mode_t mode);
3,关闭文件close()函数
int close(int fd)
- 包含头文件:unistd.h
- 返回值:成功返回0,失败返回-1
- fd: open函数返回的文件描述符
4,读取文件read()函数
4.1 用read函数从打开文件中读数据,用户可以对读入的数据进行操作。
ssize_t read(int fd, void *buf, size_t count);
read()函数从文件描述符对应的文件中读取count字节,放到buf开始的缓冲区
- 包含头文件:unistd.h
- count:表示要读取的字节的数量,通常用这个变量表示缓冲区的大小,因此count的值不要超过缓冲区的大小,否则很 容易造成缓冲区的溢出。
- buf:指针,指向缓冲区开始的位置,读入的数据将保存在这个缓冲区中
- fd:open()函数或creat()函数返回的值
- 返回值:
- 读取数据时,要判断返回实际读取数据大小来进行处理
- 0:已经到达文件末尾
- -1:读取函数有错误发生
4.2 read()函数例子
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int i;
int fd = -1;
char buf[10]; //存放数据的缓冲区
char fileName[] = "test.txt";
size_t size = -1;
fd = open(fileName, O_RDONLY);
if (-1 == fd)
{
printf("Open file %s failure!, fd:%d\n", fileName, fd);
}
else
{
printf("Open file %s success!, fd:%d\n", fileName, fd);
}
while (size)
{
size = read(fd, buf, 10);
if (size == -1)
{
close(fd);
printf("read fail error occur\n");
}
else
{
if (size > 0)
{
printf("read %d bytes:", size);
printf("\"");
for (i=0; i<size; i++)
{
printf("%c", buf[i]);
}
printf("\"\n");
}
else
{
printf("reach the end of file\n");
}
}
}
return 0;
}
5,写文件write()函数
5.1 write函数向打开的文件中写入数据,将用户的数据保存到文件中
ssize_t write(int fd, const void *buf, size_t count)
- 包含头文件:unistd.h
- fd:open()函数或creat()函数返回的值
- buf:要写入数据的指针
- count:buf大小
- 返回值
- -1:出错
- 函数操作成功会返回写入的字节数
5.2 write()函数例子
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int i;
int fd = -1;
char buf[] = "aslkdha ;lasjdlamwmd s;da,wd.,as"; //存放数据的缓冲区
char fileName[] = "test.txt";
size_t size = -1;
fd = open(fileName, O_RDWR);
if (-1 == fd)
{
printf("Open file %s failure!, fd:%d\n", fileName, fd);
}
else
{
printf("Open file %s success!, fd:%d\n", fileName, fd);
}
size = write(fd, buf, sizeof(buf));
printf("write %d byte to file %s\n", size, fileName);
return 0;
}
6,文件偏移lseek()函数
在调用read()和write()函数时,每次操作成功后,文件当前的操作位置都会移动。其中隐含了一个概念,即文件的偏移量。文件的偏移量指的是当前文件操作位置相对于文件开始位置的便宜。每次打开和对文件进行读写操作后,文件的偏移量都进行了更新。当写入数据成功时,文件的偏移量要向后移动写入数据的大小。当从文件中读出数据时,文件的偏移量要向后移动读出数据的大小。文件的偏移量是一个非负整数,表示从文件的开始到当前位置的字节数。一般情况下,对文件的读写操作都从当前的文件偏移量位置开始,并增加读写操作成功的字节数。。当打开一个文件时,如果没有指定O_APPEND选项,文件的位移量为0.如果指定了O_APPEND选项,文件的偏移量与文件的长度相等,即文件的当前操作位置移到了末尾。
6.1 lseek()函数可以设置文件偏移量的位置
off_t lseek(int fd, off_t offset, int whence);
这个函数对文件描述符fd所代表的文件,按照操作模式whence和偏移的大小offset,重新设定文件的偏移量
- 包含头文件:sys/types.h unistd.h
- whence:操作的模式
- offset:偏移值
- fd:文件描述符
whence和offset结合使用。whence表示操作的模式,offset是偏移的值
- whence为SEEK_SET,则offset为相对文件开始处的值,即将该文件偏移量设为距文件开始处offset个字节
- whence为SEEK_CUR,则offset为相对当前位置的值,即将该文件的偏移量设为其当前值加offset
- whence为SEEK_END,则offset为相对文件结尾的值,即将该文件的偏移量设置为文件长度加offset
- 返回值:
- -1:失败
- 成功返回新的文件偏移量的值,文件偏移量可以为负值
6.2lseek()函数的通用例子
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
off_t offset = -1;
offset = lseek(1, 0, SEEK_CUR); //将标准输入文件描述符的文件偏移量设为当前值
if (-1 == offset)
{
printf("STDIN can't seek\n");
}
else
{
printf("STDIN can seek\n");
}
return 0;
}
STDIN can't seek //标准输入不能进行lseek操作
6.3 空洞文件的例子
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = -1;
ssize_t size = -1;
off_t offset = -1;
char buf1[] = "01234567";
char buf2[] = "ABCDEFGH";
char fileName[] = "hole.txt";
int len = 8;
fd = open(fileName, O_RDWR|O_CREAT, S_IRWXU);
if (-1 == fd)
{
return -1;
}
size = write(fd, buf1, len);
if (size != len)
{
return -1;
}
offset = lseek(fd, 32, SEEK_SET);
if (-1 == offset)
{
printf("STDIN can't seek\n");
return -1;
}
else
{
printf("STDIN can seek\n");
}
size = write(fd, buf2, len);
if(size != len)
{
return -1;
}
close(fd);
return 0;
}
7,获得文件状态fstat()函数
7.1 在程序设计的时候经常要用到文件的一些特性值,例如文件的所有者,文件的修改时间,文件的大小等。stat()函数,fstat()函数和lstat()函数可以获得文件的状态。
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
- 包含头文件:sys/types.h,sys/stat.h,unistd.h
- statbuf:指向struct stat的指针,获得的状态从这个参数中传回
- fd:文件描述符
- 返回值
- 0:函数执行成功
- -1:有错误发生。
struct stat
{
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
ino_t st_ino; /* inode number -inode节点号*/
mode_t st_mode; /* protection -保护模式?*/
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
uid_t st_uid; /* user ID of owner -user id*/ //文件所有者的ID
gid_t st_gid; /* group ID of owner - group id*/ //文件所有者的组ID
dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
time_t st_atime; /* time of last access -最近存取时间*/
time_t st_mtime; /* time of last modification -最近修改时间*/
time_t st_ctime; /* time of last status change - */
};
7.2 stat()函数的例子
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
struct stat st;
if( -1 == stat("test.txt", &st)){ /*获得文件的状态,将状态值放入st中*/
printf("获得文件状态失败\n");
return -1;
}
printf("包含此文件的设备ID:%d\n",(int)st.st_dev); /*文件的ID号*/
printf("此文件的节点:%d\n",(int)st.st_ino); /*文件的节点*/
printf("此文件的保护模式:%d\n",(int)st.st_mode); /*文件的模式*/
printf("此文件的硬链接数:%d\n",(int)st.st_nlink); /*文件的硬链接数*/
printf("此文件的所有者ID:%d\n",(int)st.st_uid); /*文件的所有者ID*/
printf("此文件的所有者的组ID:%d\n",(int)st.st_gid); /*文件的组ID*/
printf("设备ID(如果此文件为特殊设备):%d\n",(int)st.st_rdev); /*文件的设备ID*/
printf("此文件的大小:%d\n",(int)st.st_size); /*文件的大小*/
printf("此文件的所在文件系统块大小:%d\n",(int)st.st_blksize); /*文件的系统块大小*/
printf("此文件的占用块数量:%d\n",(int)st.st_blocks); /*文件的块大小*/
printf("此文件的最后访问时间:%d\n",(int)st.st_atime); /*文件的最后访问时间*/
printf("此文件的最后修改时间:%d\n",(int)st.st_mtime); /*文件的最后修改时间*/
printf("此文件的最后状态改变时间:%d\n",(int)st.st_ctime);/*文件的最后状态改变时间*/
return 0;
}
8,文件空间映射mmap()函数
8.1 mmap()函数介绍
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
mmap()函数将文件描述符fd对应的文件中,自offset开始的一段长length的数据空间映射到内存中。
- 包含头文件:sys/mman.h
- oddset:文件中映射地址的偏执
- fd:文件描述符
- flag:用于设定映射对象的类型,选项和是否可以对映射对象进行操作,也是一个组合值,必须为MAP_SHARED或者MAP_PRIVATE二者之一
- MAP_FIXED:如果参数start指定了用于需要映射到的地址,而所指的地址无法成功建立映射,则映射失败,通常设为0.
- MAP_SHARED:共享的映射区域,映射区域允许其他进程共享,对映射区域写入数据将会写入到原来的文件中
- MAP_PRIVATE:当对映射区域进行写入操作时会产生一个映射文件的复制,即写入复制,而读操作不会影响此复制。当此映射区的修改不会写入回原来的文件,即不会影响原来文件的内容
- MAP_ANONYMOUS:建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享
- MAP_DENYWRITE:对文件的写入操作将被禁止,只能通过对此映射区操作的方式实现对文件的操作,不允许直接对文件进行操作
- MAP_LOCKED:将映射区锁定,此区域不会被虚拟内存重置
- prot:prot的值是一个组合值,可以是一个或多个。PROT_WRITE | PROT_READ的方式将映射区设置为可读写,port的设置受文件打开时的选项设置,当打开文件为只读时,写失效,读有效。
- addr:要映射到的地址,通常为NULL,表示由系统自己决定要映射到的地址
- length:映射数据的长度,即文件映射到内存中的数据大小
- PROT_EXEC:映射区域可执行
- PROT_READ:映射区域可读取
- PROT_WRITE:映射区域可写入
- PROT_NONE:映射区域不能存取
- 返回值
- 映射成功后,返回映射到的内存地址
- 映射失败后,返回值为(void*)-1
8.2 munmap()函数介绍
int munmap(void *addr, size_t length)
- 包含头文件:mman.h
- length:映射的长度
- addr:mmap()函数成功后的返回值,即映射的内存地址
8.3mmap(),munmap()函数例子
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define FILELENGTH 80
int main(void)
{
int fd = -1;
char buf[] = "quick brown fox jumps over the lazy dog";
char *ptr = NULL;
fd = open("mmap.txt", O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
if (-1 == fd)
{
return -1;
}
lseek(fd, FILELENGTH-1, SEEK_SET);
write(fd, "a", 1);
ptr = mmap(NULL, FILELENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if ((char*)-1 == ptr)
{
printf("mmap failed\n");
close(fd);
return -1;
}
memcpy(ptr+16, buf, strlen(buf));
munmap(ptr, FILELENGTH);
close(fd);
return 0;
}
9,文件属性fcntl()函数
9.1 int fcntl(int fd, int cmd, ... /* arg */ )
fcntl()函数向打开的文件fd发送命令,更改其属性
- 包含头文件:unistd.h,fcntl.h
- arg:总是一个整数,但是某些情况下使用记录锁时,则是一个指向结构的指针
- cmd:功能命令
- F_GETOWN:标志获得文件描述符fd的文件状态标志。
- F_GETFL:设置文件描述符
- F_GETFD:获得文件描述符
- F_DUPFD:用于复制文件描述符fd,获得的新文件描述符作为函数值返回。获得的文件描述符是尚未使用的文件描述符中大于或等于第3个参数值中的最小值。
- fd:文件描述符
- 返回值:命令不同,返回值也不同
- F_GETOWN:返回值如果为正数则是进程ID号,如果为负数则是进程组ID号
- F_GETFL:返回值为文件描述符的状态标志
- F_GETFD:返回值为获得的相应标识
- F_DUPFD:返回值为新的文件描述符
10,文件输入输出控制ioctl()函数
10.1 int ioctl(int d, int request, ...)
ioctl()函数通过对文件描述符发送特定的命令来控制文件描述符所代表的设备
- 包含头文件:sys/ioctl.h
- request:发送的命令
- d:一个已经打开的设备
- 返回值:
- 成功返回0或者大于1的值
- 出错返回-1
10.2 ioctl()函数例子
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
int fd = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
printf("open cdrom file\n");
return -1;
}
if (ioctl(fd, CDROMEJECT, NULL) >= 0)
{
printf("弹出CDROM成功\n");
}
else
{
printf("弹出失败\n");
}
return 0;
}