您的位置 首页 java

一文讲解linux-文件系统调用-基于C语言

是操作系统提供的、与用户程序之间的接口,也就是操作系统提供给 程序员 的接口。从感觉上系统调用类似于过程调用,都由程序代码构成,使用方式相同,但两者有实质差别:过程调用只能在用户态下运行,不能进入 核心态 ;而系统调用可以实现从用户态到核心态的转变。

一文讲解linux-文件系统调用-基于C语言

Linux 文件描述符

文件描述符 fd是进程打开文件列表中的序号,它是一个0~255的整数。文件描述符0、1、2分别用于表示标准输入、标准输出和标准错误文件。进程打开一个文件后,就一直使用文件描述符fd来对文件进行标识并进行各种操作,它是文件正在被进程使用的标志。

一文讲解linux-文件系统调用-基于C语言

open系统调用

调用 open 可以打开或创建一个文件。

 # include  <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int open(const char *pathname, int oflag, ... /* mode_t mode */);  

返回:若成功为文件描述符,若出错为- 1

我们将第三个参数写为. . .,这是 ANSI C 说明余下参数的数目和类型可以变化的方法。对于open函数而言,仅当创建新文件时才使用第三个参数。在函数原型中此参数放置在注释中。

第一个参数**pathname*是要打开或创建的文件的名字。

第二个参数oflag 参数可用来说明此函数的多个选择项。用下列一个或多个常数进行或运算构成 oflag*参数(这些常数定义在< fcntl . h >头文件中, 在这三个常数中应当只指定一个):

  • O_RDONLY 只读打开
  • O_WRONLY 只写打开
  • O_RDWR 读、写打开

第三个可选参数:

  • O_APPEND 每次写时都加到文件的尾端。
  • O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数 mode ,用其说明该新文件的存取许可权位。
  • O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在。
  • O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
  • O_NONBLOCK 如果 pathname 指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。
  • O_SYNC 使每次write都等到物理I / O操作完成。

由open函数返回的文件描述符一定是最小的未用描述符数字。

举例:

 int fd;                    //定义一个整型的文件描述符
char path[]=“/proc/version”     //记录 linux内核 版本的文件
fd=open(path,O_RDONLY)   //以只读方式打开  

更多linux内核视频教程文档资料免费领取后台私信 【内核】 自行获取.

一文讲解linux-文件系统调用-基于C语言

一文讲解linux-文件系统调用-基于C语言

close调用

可用close关闭一个打开文件:

 #include <unistd.h>

int close (int fd);  

返回:若成功为0,若出错为-1

关闭一个文件时也释放该进程加在该文件上的所有记录锁。当一个进程终止时,它所有的打开文件都由内核自动关闭。

l SEEK 调用

每个打开的文件都有一个与其相关联的“ 当前文件位移量 ”。它是一个非负整数,用以度量从文件开始处计算的字节数。通常,读、写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数。按系统默认,当打开一个文件时,除非指定O_APPEND选择项,否则该位移量被设置为0。

 #include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence) ;  

返回:若成功为新的文件位移,若出错为-1

对参数offset 的解释与参数whence的值有关

  • 若whence是SEEK_SET,则将该文件的位移量设置为距文件开始处offset 个字节
  • 若whence是SEEK_CUR,则将该文件的位移量设置为其当前值加offset, offset可为正或负
  • 若whence是SEEK_END,则将该文件的位移量设置为文件长度加offset, offset可为正或负

若lseek成功执行,则返回新的文件位移量,为此可以用下列方式确定一个打开文件的当前位移量:

 off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);  

这种方法也可用来确定所涉及的文件是否可以设置位移量。如果文件描述符引用的是一个管道或FIFO,则lseek返回-1,并将 errno 设置为EPIPE。

lseek仅将当前的文件位移量记录在内核内,它并不引起任何I / O操作。然后,该位移量用于下一个读或写操作。

文件位移量可以大于文件的当前长度,这种情况下,对该文件的下一次写将延长该文件并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被读为0。

read调用

用read从打开的文件中读数据。

 #include <unistd.h>
ssize_t read(int fd,  void  *buff, size_t nbytes) ;  

返回:读到的字节数,若已到文件尾为0,若出错为-1

write 调用

用write函数向打开文件写数据。

 #include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes) ;  

返回:若成功为已写的字节数,若出错为-1

其返回值通常与参数nbytes的值不同,否则表示出错。write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。

对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。

unlink调用

unlink()函数功能即为删除文件。执行unlink()函数会删除所给参数指定的文件。

 #include<unistd.h>
int unlink(const char *pathname); // pathname:指定要移除的链接文件。  

成功返回0;失败则返回-1,同时设置errno为相应值。

执行unlink()函数并不一定会真正的删除文件,它先会检查文件系统中此文件的连接数是否为1,如果不是1说明此文件还有其他链接对象,因此只对此文件的连接数进行减1操作。若连接数为1,并且在此时没有任何进程打开该文件,此内容才会真正地被删除掉。在有进程打开此文件的情况下,则暂时不会删除,直到所有打开该文件的进程都结束时文件就会被删除。

文件锁

fcntl函数可以改变已经打开文件的性质。

 #include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd,.../* int arg * / );

  

返回:若成功则依赖于cmd(见下),若出错为-1

  • fcntl函数有5种功能:
  • 复制一个现有的描述符(cmd=F_DUPFD).
  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
  • 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
  • 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
  • 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

当多个用户共同使用、操作一个文件也即文件共享的情况下,Linux通常采用给文件上锁的方法来避免产生竞争。

文件锁的数据结构:

  struct  flock{
    short  l_type;  //  锁类型,可取值为F_RDLCK、F_WRLCK、F_UNLCK
   off_t l_start;  // 相对偏移量
   short l_whence; // 位移量起点:SEEK_SET文件开头、SEEK_CUR文件指针当前位置、SEEK_END文件尾  
   off_t l_len;   //  加锁区域长度
   pid_t l_pid;   //  加锁进程的进程号
}  

拓展

/proc 文件系统是一种内核和内核模块用来向进程 (process) 发送信息的机制。这个伪文件系统让你可以和内核内部数据结构进行交互,获取有关进程的有用信息,在运行中改变设置。与其他文件系统不同,/proc 存在于内存之中而不是硬盘上。proc 文件系统可以被用于收集有用的关于系统和运行中的内核的信息。

下面是一些重要的文件:

  • /proc/cpuinfo – CPU 的信息 (型号, 家族, 缓存大小等)
  • /proc/meminfo – 物理内存、交换空间等的信息
  • /proc/mounts – 已加载的文件系统的列表
  • /proc/devices – 可用设备的列表
  • /proc/filesystems – 被支持的文件系统
  • /proc/modules – 已加载的模块
  • /proc/version – 内核版本
  • /proc/cmdline – 系统启动时输入的内核命令行参数

有一些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程在/proc下都对应一个以进程号为目录名的目录/proc/pid,它们是读取进程信息的接口。

通过 /proc 中可读写的文件提供了对内核的交互机制。写这些文件可以改变内核的状态。大部分 /proc 的文件是只读的,/proc/sys 目录存放所有可读写的文件的目录,可以被用于改变内核行为, /proc/sys/kernel目录包含反通用内核行为的信息。

实验

编写两个源代码文件read_lock.c和write_lock.c,分别调用lock_set函数对文件加读锁和写锁。

 // lock.c 实现加锁
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int lock_set(int fd,int type)
{
	struct flock old_lock,lock;

	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_type = type;
	lock.l_pid = -1;

	fcntl(fd,F_GETLK,&lock);
	if(lock.l_type != F_UNLCK)
	{
		if(lock.l_type == F_RDLCK)
			printf("Read lock already set by %dn",lock.l_pid);
		else if(lock.l_type == F_WRLCK)
			printf("Write lock already set by %dn",lock.l_pid);
	}

	lock.l_type = type;

	if((fcntl(fd,F_SETLKW,&lock)) < 0)
	{
		printf("Lock failed:type = %dn",lock.l_type);
		return 1;
	}

	switch(lock.l_type)
	{
		case F_RDLCK:
			printf("Read lock set by %dn",getpid());
			break;
		case F_WRLCK:
			printf("Write lock set by %dn",getpid());
			break;
		case F_UNLCK:
			printf("Release lock set by %dn",getpid());
			return 1;
			break;
		default:
			break;
	}
	return 0;
}// 读锁
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>
#include "lock.c"

int main(){
	 char  filepath[] = "./test";
	int fd = open(filepath, O_RDONLY);
	lock_set(fd, F_RDLCK);

	getchar();
	return 0;
}// 写锁
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>
#include "lock.c"

int main(){
	char filepath[] = "./test";
	int fd = open(filepath, O_WRONLY);
	lock_set(fd, F_WRLCK);
	
	getchar();
	return 0;
}  

原文地址:

文章来源:智云一二三科技

文章标题:一文讲解linux-文件系统调用-基于C语言

文章地址:https://www.zhihuclub.com/183568.shtml

关于作者: 智云科技

热门文章

网站地图