文件I/O(3)--文件共享

文件共享

文件表条目(file-table-entry):

   1.文件状态标志(file-status-flags): 读/写/追加/同步/非阻塞等;

   2.当前文件偏移量

   3.v节点指针

一个进程打开了两个文件

一个进程两次打开一个文件时,文件表是不同的,文件状态标志可能是不同的,例如读与写标志

//验证
int main(int argc, char *argv[])
{
    int fd1 = open("test.txt", O_RDONLY);
    if (fd1 == -1)
        err_exit("fd1 open O_RDONLY error");
    int fd2 = open("test.txt", O_RDWR);
    if (fd2 == -1)
        err_exit("fd2 open O_RDWR error");
 
    //读取fd1
    char buf[BUFSIZ];
    if (read(fd1, buf, 10) == -1)
        err_exit("read fd1 error");
    cout << "fd1: " << buf << endl;
 
    //读取fd2
    bzero(buf, 10);
    if (read(fd2, buf, 10) == -1)
        err_exit("read fd1 error");
    cout << "fd2: " << buf << endl;
 
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);
    write(fd2, "Helloworld", 10);
    bzero(buf, 10);
    if (read(fd1, buf, 10) == -1)
        err_exit("read fd1 error");
    cout << "after fd2 write: " << buf << endl;
}

两个进程打开同一个文件

复制文件描述符

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup  返回的新文件描述符一定是当前可用文件描述符的最小数值

dup2 可以指定新文件描述符

//示例
int main(int argc, char *argv[])
{
    int fd = open("text.txt", O_WRONLY|O_TRUNC);
    if (fd == -1)
        err_exit("open O_WRONLY error");
 
//    close(1);   //将标准输出关闭, 则文件描述符1将空闲
//    int dupfd = dup(fd);   文件重定向  标准输出是text.txt
    int dupfd = dup2(fd, 1);
    cout << "dupfd = " << dupfd << endl;
}

fcntl:操纵文件描述符,改变已打开的文件的属性

int fcntl(int fd, int cmd, ... /*int arg */ );

当cmd=F_DUPFD 表示复制文件描述符,第三个参数可以指定从那个文件描述符开始搜索空闲文件描述符

fcntl常用操作(cmd常用取值)

F_DUPFD (long)

复制文件描述符

F_GETFD (void)

F_SETFD (long)

文件描述符标志

F_GETFL (void)

F_SETFL (long)

文件状态标志

F_GETLK

F_SETLK,F_SETLKW(阻塞)

文件锁

//示例: 复制文件描述符
int main(int argc, char *argv[])
{
    int fd = open("text.txt", O_WRONLY|O_TRUNC);
    if (fd == -1)
        err_exit("open O_WRONLY error");
 
    close(1);   //将标准输出关闭, 则文件描述符1将空闲
    // 当cmd使用F_DUPFD时, 第三个参数代表搜索的起始位置
    int dupfd = fcntl(fd, F_DUPFD, 1);  // 1代表: 从1开始搜索一个空闲的文件描述符
    if (dupfd < 0)
        err_exit("fcntl F_DUPFD error");
    cout << "dupfd = " << dupfd << endl;
}

文件状态标志

F_GETFL (void)  获取文件状态标志

 Get the file access mode and the file status flags; arg is ignored.

F_SETFL (int)  设置文件状态标志

 Set the file status flags to  the  value  specified  by  arg.   File  access  mode(O_RDONLY,  O_WRONLY,  O_RDWR)  and  file  creation  flags (i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored.  On Linux this command can change only  the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.

//示例: 给文件描述符0设置成非阻塞模式
//默认条件下标准输入是阻塞式的 按下回车后有效
int main(int argc, char *argv[])
{
    char buf[1024]={0};
    int flags = fcntl(0, F_GETFL, 0);   //首先获取文件描述符0的文件状态
    if (flags == -1)
        err_exit("fcntl get error");
    flags |= O_NONBLOCK;                 //设置为非阻塞式
    if (fcntl(0, F_SETFL, flags) == -1)  //设置状态
        err_exit("fcntl set error");

    if (read(0, buf, sizeof(buf)) == -1)
        err_exit("read STDIN_FILENO error");   //非阻塞 直接出错
    cout << "buffer size = " << strlen(buf) << endl;
    cout << buf << endl;
}
//示例: 文件状态设置与清除(函数封装)
void set_flag(int fd, int setFlag)
{
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1)
        err_exit("fcntl get flags error");
    //设置状态
    flags |= setFlag;
    if (fcntl(fd, F_SETFL, flags) == -1)
        err_exit("fcntl set flags error");
}
void clr_flag(int fd, int clrFlag)
{
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1)
        err_exit("fcntl get flags error");
    //清除状态
    flags &= ~clrFlag;
    if (fcntl(fd, F_SETFL, flags) == -1)
        err_exit("fcntl set flags error");
}
 
//测试
int main(int argc, char *argv[])
{
    set_flag(0, O_NONBLOCK);
    clr_flag(0, O_NONBLOCK);
    char buf[BUFSIZ];
    if (read(0, buf, sizeof(buf)) == -1)
        err_exit("read STDIN_FILENO error");
    cout << "buffer size = " << strlen(buf) << endl;
    cout << buf << endl;
}

文件锁


       F_SETLK (struct flock *)  如果锁以及被另外一个进程施加,则出错
              Acquire  a  lock  (when l_type is F_RDLCK or F_WRLCK) or release a lock (when l_type is F_UNLCK) on the bytes specified by the l_whence, l_start, and l_len fields of lock.  If a conflicting lock is held by another
              process, this call returns -1 and sets errno to EACCES or EAGAIN.

       F_SETLKW (struct flock *) 如果锁以及被另外一个进程施加,则阻塞,直到另外一个进程解锁
              As for F_SETLK, but if a conflicting lock is held on the file, then wait for that lock to be released.  If a signal is caught while waiting, then the call is interrupted and (after the signal handler has returned)
              returns immediately (with return value -1 and errno set to EINTR; see signal(7)).

       F_GETLK (struct flock *)
              On  input  to  this  call, lock describes a lock we would like to place on the file.  If the lock could be placed, fcntl() does not actually place it, but returns F_UNLCK in the l_type field of lock and leaves the
              other fields of the structure unchanged.  If one or more incompatible locks would prevent this lock being placed, then fcntl() returns details about one of these locks in the l_type, l_whence, l_start,  and  l_len    fields of lock and sets l_pid to be the PID of the process holding that lock.

//文件锁结构体
struct flock
{
    ...
    short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
    short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start;   /* Starting offset for lock */
    off_t l_len;     /* Number of bytes to lock */
    pid_t l_pid;     /* PID of process blocking our lock
                                   (F_GETLK only) */
    ...
};

注意: 

Specifying 0 for l_len has the special meaning: lock all bytes starting  at  the  location  specified  by l_whence and l_start through to the end of file, no matter how large the file grows.

//示例1
int main(int argc, char *argv[])
{
    int fd = open("test.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
    if (fd == -1)
        err_exit("open file error");
 
    struct flock lock;
    lock.l_type = F_WRLCK;  //设定独占锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; //锁定全部文件
 
    //if (fcntl(fd, F_SETLK, &lock) == 0)   //对比下面
    if (fcntl(fd, F_SETLKW, &lock) == 0)
    {
        cout << "file lock success, press any key to unlock..." << endl;
        cin.get();
 
        lock.l_type = F_UNLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        if (fcntl(fd, F_SETLK, &lock) == -1)
            err_exit("file unlock error");
        else
            cout << "file unlock success" << endl;
    }
    else
        err_exit("file lock error");
}
//示例2: 加锁失败打印锁进信息
int main(int argc, char *argv[])
{
    int fd = open("test.txt", O_RDWR|O_CREAT|O_TRUNC, 0666);
    if (fd == -1)
        err_exit("open file error");
 
    struct flock lock;
    lock.l_type = F_WRLCK;  //设定独占锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; //锁定全部文件
 
    if (fcntl(fd, F_SETLK, &lock) == 0)
    {
        cout << "file lock success, press any key to unlock..." << endl;
        cin.get();
 
        lock.l_type = F_UNLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        if (fcntl(fd, F_SETLK, &lock) == -1)
            err_exit("file unlock error");
        else
            cout << "file unlock success" << endl;
    }
    else    //如果加锁失败, 则获取锁信息
    {
        if (fcntl(fd, F_GETLK, &lock) == -1)
            err_exit("get lock error");
 
        cout << "lock process: " << lock.l_pid << endl;
        if (lock.l_type == F_WRLCK)
            cout << "type: F_WRLCK" << endl;
        else
            cout << "type: F_RDLCK" << endl;
 
        if (lock.l_whence == SEEK_SET)
            cout << "whence: SEEK_SET" << endl;
        else if (lock.l_whence == SEEK_END)
            cout << "whence: SEEK_END" << endl;
        else
            cout << "whence: SEEK_CUR" << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/Alatebloomer/article/details/81347235