文件/目录管理(2)---dup()/dup2()/lseek()/fcntl()

1 dup()/dup2()

dup()和dup2()都可以复制文件描述符,区别是:
  dup()返回的是系统帮忙查找的未使用的最小值
  dup2()返回的是第二个参数,如果该值已经被使用,会先关闭然后再使用。

dup() dup2() 复制文件描述符,但不复制对应的文件表。
这里写图片描述

  使用文件描述符时,内存中对应一个文件表,在文件表中,会记录关于内存中文件表的信息和硬盘上的文件的信息,其中,i节点是文件在硬盘上的地址。

dup.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
  int fd = open("a.txt",
        O_RDWR|O_TRUNC|O_CREAT,0666);
  if(fd==-1) perror("open"),exit(-1);
  int fd2 = dup(fd);
  printf("fd=%d,fd2=%d\n",fd,fd2);
  write(fd,"1",1);
  write(fd2,"2",1);
  int fd3 = open("b.txt",O_RDWR|O_CREAT,0666);
  int fd4 = dup2(fd,fd3);
  printf("fd3=%d,fd4=%d\n",fd3,fd4);
  write(fd4,"Hehe",4);
  close(fd); 
  close(fd3);
  close(fd4);
}

2 lseek()函数

lseek() 是fseek()的底层实现,是系统调用。
第一个参数是fd
第二个参数是偏移量
第三个参数是偏移的起始位置,有3种选择:
  SEEK_SET - 从文件头开始
  SEEK_CUR - 从当前位置开始
  SEEK_END - 从文件尾开始
开发中,更多使用 SEEK_SET。

lseek.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
  int fd = open("a.txt",O_RDWR);
  char ch;
  read(fd,&ch,1); printf("ch=%c\n",ch);//a
  read(fd,&ch,1); printf("ch=%c\n",ch);//b
  lseek(fd,3,SEEK_CUR);
  read(fd,&ch,1); printf("ch=%c\n",ch);//f
  lseek(fd,0,SEEK_SET);write(fd,"1",1);//a->1
  lseek(fd,3,SEEK_SET);write(fd,"2",1);//d->2 
  lseek(fd,2,SEEK_CUR);write(fd,"3",1);//g->3
  lseek(fd,-2,SEEK_CUR);write(fd,"4",1);//f->4
  /*文件最后用Vi编辑,自动加有"\n",还有文件结束符EOF*/
  lseek(fd,-3,SEEK_END);write(fd,"5",1);//s->5
  close(fd);
}

  在向文件中写时,有缓冲区,并不是我们写了,马上磁盘上就有相应的数据。有相应的函数来设置同步。

writefile.c

#include <stdio.h>

int main(){
  int i;
  FILE* file = fopen("c.txt","w");
  for(i=0;i<10;i++){
    fprintf(file,"%d",i);
    //fflush(file);
    }
  sleep(20);
  fclose(file);
  printf("over\n");
}

3 fcntl()函数

fcntl(int fd,int cmd,…)
  1 可以复制文件描述符 (cmd取F_DUPFD)
  2 可以获取/设置文件的状态(cmd取F_GETFL/SETFL)
  3 实现文件锁(cmd取F_SETLK/F_GETLK/F_SETLKW)

当复制文件描述符时,和dup2类似,不同点:
  a 不会强制关闭已经使用的描述符
  b 返回值是大于等于第三个参数

3.1 获取/设置文件的状态

  获取/设置文件状态时,创建文件的标识不能获取也不能设置(O_CREAT/O_TRUNC/O_EXCL),文件的访问权限标识不能修改。能设置或者修改APPEND标识。

fcntl.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
  int fd = open("a.txt",
        O_RDONLY|O_CREAT|O_APPEND,0666);
  if(fd==-1) perror("open"),exit(-1);
  int fd2 = fcntl(fd,F_DUPFD,5);
  int fd3 = fcntl(fd,F_DUPFD,5);
  printf("fd2=%d,fd3=%d\n",fd2,fd3);//复制
  int flags = fcntl(fd,F_GETFL);
  printf("flags=%d\n",flags);//没有创建标识
  if(flags & O_APPEND) printf("APPEND\n");
  //权限标识如何判断?
  if((flags&3)==O_RDONLY) printf("RDONLY\n");
  fcntl(fd,F_SETFL,O_RDWR);//设置
  flags = fcntl(fd,F_GETFL);//再次获取
  if(flags & O_APPEND) printf("append\n");
  if((flags&3)==O_RDONLY) printf("rdonly\n");
  if((flags&3)==O_RDWR) printf("rdwr\n");
  close(fd);
  close(fd2); 
  close(fd3);
}

经验:
  位与运算经常用于取某一位二进制的值和取某几位二进制的值。比如:取a的最后8位二进制:a&0xFF
  最大int:0x7FFFFFFF,最小int:0x80000000
  至于为何最小int是0x80000000,见(http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html)
这里写图片描述

3.2 文件锁

文件锁 - 用来锁定对文件的读写操作
现在主流的锁都是读写锁,由两种锁组成:
  1 读锁 是共享锁,锁定其他进程的写操作,允许其他进程读。
  2 写锁 是独占锁,锁定其他进程的操作(无论读写)

文件锁对应一个结构,内容:
struct flock{
  short l_type; //锁的类型
  short l_whence;//锁的开始位置的参照点
  int l_start;//锁的开始位置的偏移量
  int l_len;//锁定的长度,字节为单位
  pid_t l_pid;//加锁的进程ID
};
l_type包括三种:F_RDLCK 读锁  F_WRLCK 写 锁   F_UNLCK 释放锁
l_whence和l_start 联合决定锁定的开始位置,比如:l_whence = SEEK_SET l_start=10;从文件开始第10个字节上锁。
l_pid只在F_GETLK时有效,其他时候置 -1 即可。

lock1.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd = open("a.txt",O_RDWR|O_CREAT,0666);
    if(fd==-1) perror("open"),exit(-1);
    struct flock lock;
    lock.l_type = F_WRLCK/*F_RDLCK*/;//读锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 10;
    lock.l_len = 20;
    lock.l_pid = -1;//一般给-1
    int res = fcntl(fd,F_SETLK,&lock);
    if(res == -1) perror("lock");
    else printf("lock ok\n");
    sleep(20);//lock2不需要sleep
    close(fd);
}
lock2.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd = open("a.txt",O_RDWR);
    if(fd==-1) perror("open"),exit(-1);
    struct flock lock;
    lock.l_type = F_WRLCK;//写锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 10;
    lock.l_len = 20;
    lock.l_pid = -1;//一般给-1
    int res = fcntl(fd,F_SETLK,&lock);
    if(res == -1) perror("lock");
    else printf("lock ok\n");
    close(fd);
}

  编译上面两个程序:gcc lock1.c -oa,gcc lock2.c -ob,先运行a,加上读锁,再运行b,可以加上读锁,但是加上写锁就不行。
  上面的两个程序都是程序结束后,自动释放锁,下面在程序中来实现解锁。

lock3.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd = open("a.txt",O_RDWR|O_CREAT,0666);
    if(fd==-1) perror("open"),exit(-1);
    struct flock lock;
    lock.l_type = F_RDLCK;//读锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 10;
    lock.l_len = 20;
    lock.l_pid = -1;//一般给-1
    int res = fcntl(fd,F_SETLK,&lock);//加锁
    if(res == -1) perror("lock");
    else printf("lock ok\n");
    sleep(20);//lock2不需要sleep
    printf("文件读完了\n");
    lock.l_type = F_UNLCK;
    res = fcntl(fd,F_SETLK,&lock);//释放锁
    if(res == -1) perror("release");
    else printf("release ok\n");
    sleep(15);//模拟读完文件后作的事情
    close(fd);
}

  文件锁不能锁定硬盘上的文件,不能锁定read/write函数,只能阻止其他进程的上锁行为。
  文件锁的正确用法:在调用读函数read()之前应该上读锁,在调用写函数之前上写锁。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd = open("a.txt",O_RDWR);
    if(fd==-1) 
        perror("open"),exit(-1);
    struct flock wlock;
    wlock.l_type = F_WRLCK;
    wlock.l_whence = SEEK_SET;
    wlock.l_start = 0;
    wlock.l_len = 20;
    wlock.l_pid = -1;
    int res = fcntl(fd,F_SETLK,&wlock);//先上锁
    if(res==-1) 
        perror("lock");
    else//再调写函数
        write(fd,"hello worldabcdefg",18);
    close(fd);
}

  使用文件锁时,其他进程默认是加不上锁会返回错误,可以用F_SETLKW实现加不上锁继续等待的效果。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd = open("a.txt",O_RDWR);
    if(fd==-1) 
        perror("open"),exit(-1);
    struct flock wlock;
    wlock.l_type = F_WRLCK;
    wlock.l_whence = SEEK_SET;
    wlock.l_start = 0;
    wlock.l_len = 20;
    wlock.l_pid = -1;
    int res = fcntl(fd,F_SETLKW,&wlock);//先上锁
    if(res==-1) 
        perror("lock");
    else//再调写函数
        write(fd,"hello worldabcdefg",18);
    close(fd);
}

  fcntl的cmd为F_GETLK时,不是获取锁,而是测试某个锁能否加上,不会真正的加锁。
如果锁可以加,调用F_GETLK的结果:
  struct flock的其他数据不变,锁的类型l_type变为F_UNLCK。
如果锁不可以加,调用F_GETLK的结果:
  struct flock的数据都会发生改变,锁的类型、锁的l_whence、l_start、l_len变成 当前正在起作用的锁的相关信息,l_pid 变成当前正在加锁的进程ID。(不是测试的进程)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd = open("a.txt",O_RDWR);
    if(fd==-1) 
        perror("open"),exit(-1);
    struct flock rlock;
    rlock.l_type = F_RDLCK;
    rlock.l_whence = SEEK_SET;rlock.l_start = 0;
    rlock.l_len = 30; rlock.l_pid = -1;
    int res = fcntl(fd,F_GETLK,&rlock);//测试锁
    printf("%d,%d,%d\n",F_RDLCK,F_WRLCK,F_UNLCK);
    if(res==-1) 
        perror("rlock"),exit(-1);
    else
      printf("type=%d,start=%d,l_pid=%d\n",
    rlock.l_type,rlock.l_start,rlock.l_pid);
    rlock.l_type = F_WRLCK;//写锁
    res = fcntl(fd,F_GETLK,&rlock);//测试锁
    if(res==-1) 
        perror("rlock"),exit(-1);//
    else{
        printf("type=%d,start=%d,l_pid=%d\n",
            rlock.l_type,rlock.l_start,rlock.l_pid);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值