系统调用(四)——文件操作函数

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. 信息

  1. 作用:可以移动当前读写位置(或者说偏移量)
  2. 函数原型:off_t lseek(int fd,off_t offset,int whence)
    1. fd:文件描述符
    2. offset:偏移量
    3. whence:起始偏移位置
      • SEEK_SET
      • SEEK_CUR
      • SEEK_END
    4. 返回值
      • 成功:较起始位置偏移量
      • 失败:-1 errno

3.3. 应用

  1. 文件的“读”和“写”使用同一偏移位置。
  2. 使用lseek拓展文件:write操作才能实质性的拓展文件,单lseek是不能进行拓展的。
  3. lseek获取文件大小

3.4.简单程序

  1. 简单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;

}

  1. 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函数

  1. 为什么目录项要游离于inode之外,将文件名单独存储呢?
    其目的是为了实现文件共享。Linux允许多个目录项共享一个inode,即共享盘块。不同文件名,在人类眼中是两个文件,但是在内核中是同一个文件。
  2. link函数可以为已经存在的文件创建目录项(硬链接)
  3. int link(const char *oldpath,const char *newpath);成功返回0,失败返回-1,设置errno为相应值
  4. 注意:由于两个参数可以使用“ 相对 / 绝对 路径+文件名”的方式来指定,所以易出错。
  5. mv命令既是修改了目录项,而并不修改文件本身。
  6. 简单程序
#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函数

  1. 删除一个文件的目录项
  2. int unlink(const char *pathname);成功:0,失败:-1,设置errno为响应值
  3. 注意Linux下删除文件的机制:不断将st_nlink-1,直至减到0为止。无目录项对应的文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定)
    所以我们删除文件,只是让文件具备了被释放的条件
  4. unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍然不会马上被释放。等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。
  5. 简单程序
#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);


}

  1. 隐式回收
    当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放,系统这一特性称为隐式回收。

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为相应值。

小知识

  1. 对于2个字节有16位,前四位表示文件类型。
  2. readlink filename读符号链接文件本身。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值