1. sync/fsync/fdatasync
1)大多数磁盘 I/O 都通过缓冲进行,写入文件其实只是写入缓冲区,
直到缓冲区满,才将其排入写队列。
2)延迟写降低了写操作的次数,提高了写操作的效率,
但可能导致磁盘文件与缓冲区数据不同步。
3)sync/fsync/fdatasync 用于强制磁盘文件与缓冲区同步。
4)sync 将所有被修改过的缓冲区排入写队列即返回,不等待写磁盘操作完成。
5)fsync 只针对一个文件,且直到写磁盘操作完成才返回。
6)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
2. 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 函数那样关闭之。
F_GETFD - 获取文件描述符标志。
目前仅定义了一个文件描述符标志位 FD_CLOEXEC;
0 - 在通过 execve() 函数所创建的进程中,该文件描述符依然保持打开。
1 - 在通过 execve() 函数所创建的进程中,该文件描述符将被关闭。
F_SETFD - 设置文件描述符标志。
arg 只能取 FD_CLOEXEC (调用 exec 族函数后关闭该 fd)。
F_GETFL - 获取文件模式;不能获取 O_CREAT、O_EXCL 和 O_TRUNC。
F_SETFL - 追加文件模式;arg 只能取 O_APPEND 和 O_NONBLOCK。
范例:dup.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main (void) {
int fd1 = open ("dup1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd1 == -1) {
perror ("open");
return -1;
}
printf ("fd1 = %d\n", fd1);
int fd2 = open ("dup2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd2 == -1) {
perror ("open");
return -1;
}
printf ("fd2 = %d\n", fd2);
/* fd1 复制给 fd2,若 fd2 已经打开,则关闭,返回 fd2
int fd3 = dup2 (fd1, fd2); // fd1, fd2, fd3 分别为:3,4,4(对应同一个文件表)
if (fd3 == -1) {
perror ("dup2");
return -1;
}
*/
// fd1 复制给 fd2,由于 fd2 已经打开,所以选择一个新的:
int fd3 = fcntl (fd1, F_DUPFD, fd2); // fd1, fd2, fd3 分别为:3,4,5(3 和 5 对应同一个文件表)
if (fd3 == -1) {
perror ("fcntl");
return -1;
}
printf ("fd3 = %d\n", fd3);
const char* text = "123";
if (write (fd1, text, strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
text = "456";
if (write (fd2, text, strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
text = "789";
if (write (fd3, text, strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
close (fd3);
close (fd2);
close (fd1);
return 0;
}
分析:
int fd3 = dup2 (fd1, fd2); fd2 复制给 fd1,如果 fd2 已经打开,则先关闭 fd2,
然后再进行复制,那么 fd2 和 fd3 的文件描述符一样;且 fd1 fd2 fd3 三个文件的内容一样;
上例中,fd1 fd2 fd3 内容都为:123456789
int fd3 = fcntl (fd1, F_DUPFD, fd2); 同样把 fd2 复制给 fd1,如果发现 fd2 打开,
则另外再找一个最小文件描述符给 fd3,所以 fd1 fd2 fd3 文件描述符分别为:3 4 5;
同时,fd1 和 fd3 的内容一样,为:123789,fd2 内容为:456;
范例:flags.c
#include <stdio.h>
#include <fcntl.h>
void pflags (int flags) {
printf ("文件模式(%08X):", flags); // %08x 十六进制
struct {
int flag;
const char* desc;
} flist[] = {
O_RDONLY, "O_RDONLY",
O_WRONLY, "O_WRONLY",
O_RDWR, "O_RDWR",
O_APPEND, "O_APPEND",
O_CREAT, "O_CREAT",
O_EXCL, "O_EXCL",
O_TRUNC, "O_TRUNC",
O_NOCTTY, "O_NOCTTY",
O_NONBLOCK, "O_NONBLOCK",
O_SYNC, "O_SYNC",
O_DSYNC, "O_DSYNC",
O_RSYNC, "O_RSYNC",
O_ASYNC, "O_ASYNC"
};
size_t i;
int first = 1;
for (i = 0; i < sizeof (flist) / sizeof (flist[0]); i++)
if (flags & flist[i].flag) {
printf ("%s%s", first ? "" : " | ", flist[i].desc); // 输出类似格式:O_WRONLY | O_ASYNC
first = 0;
}
printf ("\n");
}
int main (void) {
int fd = open ("flags.txt", O_WRONLY | O_CREAT | O_ASYNC, 0644); // 没有即创建且异步
if (fd == -1) {
perror ("open");
return -1;
}
int flags = fcntl (fd, F_GETFL);
if (flags == -1) {
perror ("fcntl");
return -1;
}
pflags (flags); // O_CREATE/O_EXCL/O_TRUNC 不能被 F_GETFL 获取
if (fcntl (fd, F_SETFL, O_RDWR | O_APPEND | O_NONBLOCK) == -1) {
perror ("fcntl");
return -1;
}
if ((flags = fcntl (fd, F_GETFL)) == -1) {
perror ("fcntl");
return -1;
}
pflags (flags); // 只有 O_APPEND/O_NONBLOCK 可以被 F_SETFL 追加
close (fd);
return 0;
}
3. 文件锁
#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,(阻塞模式:等待)
1)大多数磁盘 I/O 都通过缓冲进行,写入文件其实只是写入缓冲区,
直到缓冲区满,才将其排入写队列。
2)延迟写降低了写操作的次数,提高了写操作的效率,
但可能导致磁盘文件与缓冲区数据不同步。
3)sync/fsync/fdatasync 用于强制磁盘文件与缓冲区同步。
4)sync 将所有被修改过的缓冲区排入写队列即返回,不等待写磁盘操作完成。
5)fsync 只针对一个文件,且直到写磁盘操作完成才返回。
6)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
2. 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 函数那样关闭之。
F_GETFD - 获取文件描述符标志。
目前仅定义了一个文件描述符标志位 FD_CLOEXEC;
0 - 在通过 execve() 函数所创建的进程中,该文件描述符依然保持打开。
1 - 在通过 execve() 函数所创建的进程中,该文件描述符将被关闭。
F_SETFD - 设置文件描述符标志。
arg 只能取 FD_CLOEXEC (调用 exec 族函数后关闭该 fd)。
F_GETFL - 获取文件模式;不能获取 O_CREAT、O_EXCL 和 O_TRUNC。
F_SETFL - 追加文件模式;arg 只能取 O_APPEND 和 O_NONBLOCK。
范例:dup.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
int main (void) {
int fd1 = open ("dup1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd1 == -1) {
perror ("open");
return -1;
}
printf ("fd1 = %d\n", fd1);
int fd2 = open ("dup2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd2 == -1) {
perror ("open");
return -1;
}
printf ("fd2 = %d\n", fd2);
/* fd1 复制给 fd2,若 fd2 已经打开,则关闭,返回 fd2
int fd3 = dup2 (fd1, fd2); // fd1, fd2, fd3 分别为:3,4,4(对应同一个文件表)
if (fd3 == -1) {
perror ("dup2");
return -1;
}
*/
// fd1 复制给 fd2,由于 fd2 已经打开,所以选择一个新的:
int fd3 = fcntl (fd1, F_DUPFD, fd2); // fd1, fd2, fd3 分别为:3,4,5(3 和 5 对应同一个文件表)
if (fd3 == -1) {
perror ("fcntl");
return -1;
}
printf ("fd3 = %d\n", fd3);
const char* text = "123";
if (write (fd1, text, strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
text = "456";
if (write (fd2, text, strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
text = "789";
if (write (fd3, text, strlen (text) * sizeof (text[0])) == -1) {
perror ("write");
return -1;
}
close (fd3);
close (fd2);
close (fd1);
return 0;
}
分析:
int fd3 = dup2 (fd1, fd2); fd2 复制给 fd1,如果 fd2 已经打开,则先关闭 fd2,
然后再进行复制,那么 fd2 和 fd3 的文件描述符一样;且 fd1 fd2 fd3 三个文件的内容一样;
上例中,fd1 fd2 fd3 内容都为:123456789
int fd3 = fcntl (fd1, F_DUPFD, fd2); 同样把 fd2 复制给 fd1,如果发现 fd2 打开,
则另外再找一个最小文件描述符给 fd3,所以 fd1 fd2 fd3 文件描述符分别为:3 4 5;
同时,fd1 和 fd3 的内容一样,为:123789,fd2 内容为:456;
范例:flags.c
#include <stdio.h>
#include <fcntl.h>
void pflags (int flags) {
printf ("文件模式(%08X):", flags); // %08x 十六进制
struct {
int flag;
const char* desc;
} flist[] = {
O_RDONLY, "O_RDONLY",
O_WRONLY, "O_WRONLY",
O_RDWR, "O_RDWR",
O_APPEND, "O_APPEND",
O_CREAT, "O_CREAT",
O_EXCL, "O_EXCL",
O_TRUNC, "O_TRUNC",
O_NOCTTY, "O_NOCTTY",
O_NONBLOCK, "O_NONBLOCK",
O_SYNC, "O_SYNC",
O_DSYNC, "O_DSYNC",
O_RSYNC, "O_RSYNC",
O_ASYNC, "O_ASYNC"
};
size_t i;
int first = 1;
for (i = 0; i < sizeof (flist) / sizeof (flist[0]); i++)
if (flags & flist[i].flag) {
printf ("%s%s", first ? "" : " | ", flist[i].desc); // 输出类似格式:O_WRONLY | O_ASYNC
first = 0;
}
printf ("\n");
}
int main (void) {
int fd = open ("flags.txt", O_WRONLY | O_CREAT | O_ASYNC, 0644); // 没有即创建且异步
if (fd == -1) {
perror ("open");
return -1;
}
int flags = fcntl (fd, F_GETFL);
if (flags == -1) {
perror ("fcntl");
return -1;
}
pflags (flags); // O_CREATE/O_EXCL/O_TRUNC 不能被 F_GETFL 获取
if (fcntl (fd, F_SETFL, O_RDWR | O_APPEND | O_NONBLOCK) == -1) {
perror ("fcntl");
return -1;
}
if ((flags = fcntl (fd, F_GETFL)) == -1) {
perror ("fcntl");
return -1;
}
pflags (flags); // 只有 O_APPEND/O_NONBLOCK 可以被 F_SETFL 追加
close (fd);
return 0;
}
3. 文件锁
#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,(阻塞模式:等待)