c /c++复习笔记 第四天

=================================

文件系统(下)

一、 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

二、 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 (
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值