=================================
文件系统(下)
一、 sync和fsync以及fdatasync
基本介绍
大多数磁盘I/O都通过缓冲进行,
写入文件其实只是写入缓冲区,直到缓冲区满,
才将其排入写队列。延迟写降低了写操作的次数,提高了写操作的效率,
但可能导致磁盘文件与缓冲区数据不同步。sync/fsync/fdatasync用于强制磁盘文件与缓冲区同步。
sync将所有被修改过的缓冲区排入写队列即返回,
不等待写磁盘操作完成。fsync只针对一个文件,且直到写磁盘操作完成才返回。
fdatasync只同步文件数据,不同步文件属性。
函数说明
#include <unistd.h>
void sync (void);
int fsync (
int fd
);
成功返回0,失败返回-1。
int fdatasync (
int fd
);
成功返回0,失败返回-1。
解析流程
+-fwrite-> 标准库缓冲 -fflush-+ sync
应用程序内存 -+ +-> 内核缓冲 -fdatasync-> 磁盘(缓冲)
+------------write------------+ fsync
二、 fcntl
函数说明
#include <fcntl.h>
int fcntl (
int fd, // 文件描述符
int cmd, // 操作指令
... // 可变参数,因操作指令而异
);
对fd文件执行cmd操作,某些操作需要提供参数。
1. 常用形式
~~~~~~~
#include <fcntl.h>
int fcntl (int fd, int cmd);
int fcntl (int fd, int cmd, long arg);
成功返回值因cmd而异,失败返回-1。
cmd取值:
F_DUPFD - 复制fd为不小于arg的文件描述符。
若arg文件描述符已用,
该函数会选择比arg大的最小未用值,
而非如dup2函数那样关闭之。
范例:dup.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int main (void) {
int fd1 = open ("dup.txt", O_RDWR | O_CREAT |
O_TRUNC, 0664);
if (fd1 == -1) {
perror ("open");
return -1;
}
printf ("fd1 = %d\n", fd1);
int fd2 = dup (fd1);
if (fd2 == -1) {
perror ("dup");
return -1;
}
printf ("fd2 = %d\n", fd2);
int fd3 = dup2 (fd2, 100);
if (fd3 == -1) {
perror ("dup2");
return -1;
}
printf ("fd3 = %d\n", fd3);
const char* text = "Hello, World !";
if (write (fd1, text,
strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
if (lseek (fd2, -7, SEEK_CUR) == -1) {
perror ("lseek");
return -1;
}
text = "Linux";
if (write (fd3, text,
strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
close (fd3);
close (fd2);
close (fd1);
return 0;
}
F_GETFD - 获取文件描述符标志。
F_SETFD - 设置文件描述符标志。
目前仅定义了一个文件描述符标志位FD_CLOEXEC:
0 - 在通过execve()函数所创建的进程中,
该文件描述符依然保持打开。
1 - 在通过execve()函数所创建的进程中,
该文件描述符将被关闭。
F_GETFL - 获取文件状态标志。
不能获取O_CREAT/O_EXCL/O_TRUNC。
F_SETFL - 追加文件状态标志。
只能追加O_APPEND/O_NONBLOCK。
范例:flags.c
这里写代码片
2. 文件锁
~~~~~
函数说明
#include <fcntl.h>
int fcntl (int fd, int cmd, struct flock* lock);
其中:
struct flock {
short int l_type; // 锁的类型:
// F_RDLCK/F_WRLCK/F_UNLCK
// (读锁/写锁/解锁)
short int l_whence; // 偏移起点:
// SEEK_SET/SEEK_CUR/SEEK_END
// (文件头/当前位置/文件尾)
off_t l_start; // 锁区偏移,从l_whence开始
off_t l_len; // 锁区长度,0表示锁到文件尾
pid_t l_pid; // 加锁进程,-1表示自动设置
};
参数说明
cmd取值:
F_GETLK - 测试lock所表示的锁是否可加。
若可加则将lock.l_type置为F_UNLCK,
否则通过lock返回当前锁的信息。
F_SETLK - 设置锁定状态为lock.l_type,
成功返回0,失败返回-1。
若因其它进程持有锁而导致失败,
则errno为EACCES或EAGAIN。
F_SETLKW - 设置锁定状态为lock.l_type,
成功返回0,否则一直等待,
除非被信号打断返回-1。
1) 既可以锁定整个文件,也可以锁定特定区域。
2) 读锁(共享锁)、写锁(独占锁/排它锁)、解锁。
范例:lock1.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
// 加读锁
int rlock (int fd, off_t start, off_t len, int wait) {
struct flock lock;
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = start;
lock.l_len = len;
lock.l_pid = -1;
return fcntl (fd, wait ? F_SETLKW : F_SETLK,&lock);
}
// 加写锁
int wlock (int fd, off_t start, off_t len, int wait) {
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = start;
lock.l_len = len;
lock.l_pid = -1;
return fcntl (fd, wait ? F_SETLKW : F_SETLK,&lock);}
// 解锁
int ulock (int fd, off_t start, off_t len) {
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = start;
lock.l_len = len;
lock.l_pid = -1;
return fcntl (fd, F_SETLK, &lock);
}
int main (void) {
printf ("进程标识(PID):%d\n", getpid ());
int fd = open ("lock.txt",
O_RDWR | O_CREAT | O_TRUNC, 0664);
if (fd == -1) {
perror ("open");
return -1;
}
const char* text = "ABCDEFGHIJKLMNOPQR";
if (write (fd, text,
strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
// 对EFGH加读锁
printf ("对EFGH加读锁");
if (rlock (fd, 4, 4, 0) == -1) {
printf ("失败:%m\n");
return -1;
}
printf ("成功!\n");
// 对MNOP加写锁
printf ("对MNOP加写锁");
if (wlock (fd, 12, 4, 0) == -1) {
printf ("失败:%m\n");
return -1;
}
printf ("成功!\n");
printf ("按<回车>,解锁MN...");
getchar ();
// 解锁MN
ulock (fd, 12, 2);
printf ("按<回车>,解锁EFGH...");
getchar ();
// 解锁EFGH
ulock (fd, 4, 4);
close (fd);
return 0;
}
图片展示
图示:rwlock.bmp
图示: flock.bmp
3) 文件描述符被关闭(进程结束)时,自动解锁。
4) 劝谏锁(协议锁)、强制锁。
范例:lock2.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
// 打印锁
void plock (struct flock lock) {
if (lock.l_type == F_UNLCK)
printf ("没有锁。\n");
else {
printf ("%d进程", lock.l_pid);
switch (lock.l_whence) {
case SEEK_SET:
printf ("在距文件头");
break;
case SEEK_CUR:
printf ("在距当前位置");
break;
case SEEK_END:
printf ("在距文件尾");
break;
}
printf ("%ld字节处,为%ld字节加了",
lock.l_start, lock.l_len);
switch (lock.l_type) {
case F_RDLCK:
printf (