linux-文件系统调用-基于C语言
系统调用
是操作系统提供的、与用户程序之间的接口,也就是操作系统提供给程序员的接口。从感觉上系统调用类似于过程调用,都由程序代码构成,使用方式相同,但两者有实质差别:过程调用只能在用户态下运行,不能进入核心态;而系统调用可以实现从用户态到核心态的转变。
Linux文件描述符
文件描述符fd是进程打开文件列表中的序号,它是一个0~255的整数。文件描述符0、1、2分别用于表示标准输入、标准输出和标准错误文件。进程打开一个文件后,就一直使用文件描述符fd来对文件进行标识并进行各种操作,它是文件正在被进程使用的标志。
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) //以只读方式打开
close调用
可用close关闭一个打开文件:
#include <unistd.h>
int close (int fd);
返回:若成功为0,若出错为-1
关闭一个文件时也释放该进程加在该文件上的所有记录锁。当一个进程终止时,它所有的打开文件都由内核自动关闭。
lseek调用
每个打开的文件都有一个与其相关联的“当前文件位移量”。它是一个非负整数,用以度量从文件开始处计算的字节数。通常,读、写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数。按系统默认,当打开一个文件时,除非指定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 %d\n",lock.l_pid);
else if(lock.l_type == F_WRLCK)
printf("Write lock already set by %d\n",lock.l_pid);
}
lock.l_type = type;
if((fcntl(fd,F_SETLKW,&lock)) < 0)
{
printf("Lock failed:type = %d\n",lock.l_type);
return 1;
}
switch(lock.l_type)
{
case F_RDLCK:
printf("Read lock set by %d\n",getpid());
break;
case F_WRLCK:
printf("Write lock set by %d\n",getpid());
break;
case F_UNLCK:
printf("Release lock set by %d\n",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;
}
本文来自博客园,作者:漫漫长夜何时休,转载请注明原文链接:https://www.cnblogs.com/ag-chen/articles/13455228.html