文章目录
1.fcntl函数(控制属性)
1.1 头文件
#include<unistd.h>
#include<fcntl.h>
1.2. 信息
改变一个已经打开
的文件的访问控制属性。
重点掌握两个参数的使用,F_GETFL和F_SETFL.
int flags = fcntl(fd,F_GETFL);
1.3. 属性
- 获取文件状态:F_GETFL
- 设置文件状态:F_SETFL
- flags:【位图,每个二进制位表示一个信息,如一个二进制位表示O_CREAT,一个整型有32个二进制位。使用位图可以节省内存。】
1.4. 简单程序
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>
#define MSG_TRY "try again\n"
int main(int argc,char *argv[])
{
char buf[10];
int flags,n;
flags = fcntl(STDIN_FILENO,F_GETFL);
if(flags == -1){
perror("fcntl error");
exit(1);
}
flags |= O_NONBLOCK; //不管原来是否有,都会添加上
int ret = fcntl(STDIN_FILENO,F_SETFL,flags);
if(ret == -1){
perror("fcntl error");
exit(1);
}
tryagain:
n = read(STDIN_FILENO,buf,10);
if(n<0){
if(errno != EAGAIN){
perror("read /dev/tty");
exit(1);
}
sleep(3);
write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY));
goto tryagain;
}
write(STDOUT_FILENO,buf,n);
return 0;
}
2.ioctl函数
对设备I/O通道进行管理,控制设备特性。【主要应用于设备驱动程序中】
通常用来获取文件的【物理特性】(该特性,不同文件类型所含有的值各不相同)
3.lseek函数(文件偏移)
3.1. 头文件
#include<sys/types.h>
#include<unistd.h>
3.2. 信息
- 作用:可以移动当前读写位置(或者说偏移量)
- 函数原型:
off_t lseek(int fd,off_t offset,int whence)
- fd:文件描述符
- offset:偏移量
- whence:起始偏移位置
SEEK_SET
SEEK_CUR
SEEK_END
- 返回值
- 成功:较起始位置偏移量
- 失败:-1 errno
3.3. 应用
- 文件的“读”和“写”使用同一偏移位置。
- 使用lseek拓展文件:write操作才能实质性的拓展文件,单lseek是不能进行拓展的。
- lseek获取文件大小
3.4.简单程序
- 简单lseek读写程序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
int fd,n;
char msg[] = "It is a test for lseek\n";
char ch;
fd = open("lseek.txt",O_RDWR|O_CREAT,0644);
if(fd<0){
perror("open lseek.txt error");
exit(1);
}
write(fd,msg,strlen(msg));
lseek(fd,0,SEEK_SET);
while((n = read(fd,&ch,1))){
if(n<0){
perror("read error");
exit(1);
}
write(STDOUT_FILENO,&ch,n); //将文件内容按字节读出,写出到屏幕
}
close(fd);
return 0;
}
- sleek获得文件大小
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
int fd = open(argv[1],O_RDWR);
if(fd == -1){
perror("open error");
exit(1);
}
int length = lseek(fd,0,SEEK_END);
printf("file size:%d\n",length);
return 0;
}
3.5.拓展
od -tcx filename 查看文件的16进制表示形式
od -tcd filename 查看文件的10进制表示形式
4.truncate函数(拓展文件)
4.1. 作用:只能拓展一个现有文件的大小。
4.2. 函数原型:
int truncate(const char *path, off_t length);
int ftruncate(int fd,off_t length);
5.传入传出参数
5.1. 传入参数:const
关键字修饰的指针变量
在函数内部读
操作,例如:char* strcpy(const char *src,char *dst);
5.2. 传出参数:
- 指针作为函数参数
- 函数调用前,指针指向的空间可以无意义,调用后指针指向的空间有意义,且作为函数的返回值传出
- 在函数内部写操作
5.3. 传入传出参数:
- 调用前指向的空间有实际意义
- 调用期间在函数内读、写(改变原值)操作
- 作为函数返回值传出
- 例如:char *strtok_r(char *str,const *delim,char **saveptr);
以下是关于文件存储函数
使用man手册查看函数时可以看到有的会给出样例程序:
man 2 stat
然后按G
即可查看样例程序
了解inode、dentry、数据存储、文件系统
6.inode
- 概念:其本质为结构体,存储文件的属性信息,如:权限、类型、大小、时间、用户、盘块位置…也叫作文件属性管理结构,大多数的inode都存储在磁盘中。
- 少量使用、近期使用的inode会被缓存到内存中。
- 使用
stat filename
可以查看inode信息,如:
- 文件名和inode编号存储在dentry(目录项)
- 如果没有目录项指向inode,也没有inode指向磁盘位置,则对应的磁盘空间不会被擦除而是覆盖。
7.dentry
- 目录项,其本质依然是结构体,重要成员变量有两个{文件名,inode,…},而文件内容(data)保存在磁盘盘块中。
8.stat函数
- 作用:获取文件属性,(从inode结构体获取)
int stat (const char *path,struct stat*buf);
成功返回0,失败返回-1.设置error为恰当值。- 参数1:文件名
- 参数2:inode结构体指针(
传出参数
)
- 文件属性将通过传出参数返回给调用者
- 使用stat函数查看文件属性
例如获取一个文件大小(通过man 2 stat
可以查看该函数结构体里面的内容):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
struct stat sbuf;
int ret = stat(argv[1],&sbuf);
if(ret == -1){
perror("stat error");
exit(1);
}
printf("file size:%ld\n",sbuf.st_size);
return 0;
}
查看文件的模式:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
struct stat sb;
int ret = stat(argv[1],&sb);
if(ret == -1){
perror("stat error");
exit(1);
}
if(S_ISREG(sb.st_mode)){
printf("It is a regular file\n");
}else if(S_ISDIR(sb.st_mode)){
printf("It is a dir\n");
}else if(S_ISFIFO(sb.st_mode)){
printf("It is a pipe\n");
}else if(S_ISLNK(sb.st_mode)){
printf("It is a sym link\n");
}
return 0;
}
- 默认stat函数可以穿透符号链接
9.lstat函数
int lstat(const char *path,struct stat*buf);
成功返回0,失败返回-1,设置errno为恰当值。- 练习:给指定文件名,判断文件类型(将上面的stat函数改为lstat函数即可)
- 文件类型判断方法:st_mode取高四位,但应使用宏函数:
- S_ISREG(m)---->is it a regular file
- lstat不会穿透符号链接
10.access函数、chmod函数、truncate函数
- access函数:测试指定文件是否存在/拥有某种权限。
- chmod函数:修改文件访问权限。
- truncate函数:截断文件长度成指定长度。常用来拓展文件大小,代替lseek。
11.link函数
- 为什么目录项要游离于inode之外,将文件名单独存储呢?
其目的是为了实现文件共享。Linux允许多个目录项共享一个inode,即共享盘块。不同文件名,在人类眼中是两个文件,但是在内核中是同一个文件。 - link函数可以为已经存在的文件创建目录项(硬链接)
int link(const char *oldpath,const char *newpath);
成功返回0,失败返回-1,设置errno为相应值- 注意:由于两个参数可以使用“ 相对 / 绝对 路径+文件名”的方式来指定,所以易出错。
- mv命令既是修改了目录项,而并不修改文件本身。
- 简单程序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
link(argv[1],argv[2]);
unlink(argv[1]);
return 0;
}
12.unlink函数
- 删除一个文件的目录项
int unlink(const char *pathname);
成功:0,失败:-1,设置errno为响应值- 注意Linux下删除文件的机制:不断将st_nlink-1,直至减到0为止。无目录项对应的文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定)
所以我们删除文件,只是让文件具备了被释放的条件
。 - unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍然不会马上被释放。等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。
- 简单程序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
//unlink函数是删除一个dentry
int main(int argc,char *argv[])
{
int fd;
char *p = "test of unlink\n";
char *p2 = "after write something\n";
fd = open("temp.txt",O_RDWR|O_CREAT|O_TRUNC,0644);
if(fd<0){
perror("open temp error");
exit(1);
}
int ret = unlink("temp.txt");
if(ret < 0){
perror("unlink error");
exit(1);
}
ret = write(fd,p,strlen(p));
if(ret == -1){
perror("------write error");
exit(1);
}
printf("hi!I am printf\n");
ret = write(fd,p2,strlen(p2));
if(ret == -1){
perror("------write error");
exit(1);
}
printf("Enter anykey continue\n");
getchar();
close(fd);
}
- 隐式回收
当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放,系统这一特性称为隐式回收。
13.readlink函数
读取符号链接文件本身的内容,得到链接所指向的文件名。
ssize_t readlink(const char *path,char *buf,size_t bufsiz);
成功返回实际读到的字节数,失败返回-1,设置errno为相应值。
14.rename函数
重命名一个文件
int rename(const char *oldpath,const char *newpath);
成功:0,失败:-1,设置errno为相应值。
小知识
- 对于2个字节有16位,前四位表示文件类型。
readlink filename
读符号链接文件本身。