Linux应用层开发(二)文件I/O

标准I/O实际上是在文件I/O的基础上封装了缓冲机制,标准I/O不是系统调用。
标准I/O的好处:减少了系统开销,先访问缓冲区,必要的时再访问实际文件
标准I/O的坏处:不能实时操作文件,大部分情况都在操作缓冲区,只能访问普通文件,设备文件,管道文件等不能访问。

文件I/O是系统调用
文件I/O的好处:可以实际实时操作文件
文件I/O的坏处:加大了系统了开销

支持POSIX规范的,都支持文件I/O操作
linux系统成功的关键因素就是与其他操作系统和谐共存的能力,
Linux文件系统由两层构成:第一层是虚拟文件系统(VFS),第二层是各种不同的具体的文件系统
它将具体的文件系统公共部分抽取出来形成一层抽象层,是系统内核的一部分,它位于用户和具体的文件系统之间,它对用户程序提供了标准的文件系统调用接口。
对具体的文件系统操作,它通过函数指针调用不同的文件系统函数,完成对应的操作。
以下是VFS的相关图解:

在这里插入图片描述

文件描述符:是一个非负的整数,它是一个索引值,并指向在内核中每个进程打开文件的记录表。当打开一个现存或者创建一个新文件的时候,内核就会返回一个文件描述符号。

打开文件:int open(const char *pathname,int flags,int perms);

头文件:

#include<sys/stat.h>
#include<fcntl.h>

返回值:成功:返回文件描述符,失败:返回-1
第一参数:pathname被打开的文件名(可包括路径名)
第二个参数:flags:文件被打开的方式
第三个参数:perms新建文件的存取权限

flags文件的打开方式
O_RDONY以只读方式打开文件
O_WRONLY以只写方式打开文件
O_RDWR以读写方式打开文件
o_CREAT如果该文件不存在就创建一个新的文件,并用第3个参数为其设置权限
O_EXCL如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在
O_NOCTTY使用本参数时,若打开的是终端文件,那么该终端不会成为当前进程的控制终端
O_TRUNC若文件已存在,那么会删除文件中的全部数据,并且设置文件大小为0
O_APPEND以追加的方式打开文件,在写文件的时候,文件读写位置自动指向文件的末尾,也就是说讲写入数据添加到文件的末尾

----------------------------分割线----------------------------------------------------------------

perms新建文件的存取权限

可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH)其中R/W/X分别表示读/写/执行权限|
USR/GSR/OTH分别表示文件所有者/文件所属组/其他用户例如,S_IRUSR|S_IWUSR表示设置文件所有者的可读写属性。八进制表示法中0600表示同样的权限。

关闭文件:int close(int fd);

头文件:#inlcude<unisrd.h>
返回值:成功:返回0,失败:返回-1
参数:文件描述符

例子:

#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
	int fd;
	if((fd=open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666))<0)
	{
		perror("fail to open\n");
		return -1;
	}
	close(fd);
	return 0;
}

文件读:size_t read(int fd,void *buf,size_t count);

头文件:#include<unistd.h>
返回值:成功:读到的字节数; 0:已到达文件末尾 ;-1:出错
第一个参数:文件描述符
第二个参数:指定存储器读出数据的缓冲区
第三个参数:指定读出的字节数

文件读:size_t write(int fd,void *buf,size_t count);

头文件:#include<unistd.h>
返回值:成功:已写的字节数;-1:出错
第一个参数:文件描述符
第二个参数:指定存储器写入数据的缓冲区
第三个参数:指定写入的字节数

例子:

#include<stdio.h>
   #include<unistd.h>
   #include<sys/stat.h>
   #include<fcntl.h>
   #define N 64
   int file_copy(const char *s_file,const char *d_file)
   {
       int fd_s,fd_d;
       int r_count=0;
      int buf[N];
      if(-1==(fd_s=open(s_file,O_CREAT|O_RDONLY,0600)))
      {
          perror("open s_file_faile\n");
          return -1;
      }
      if(-1==(fd_d=open(d_file,O_CREAT|O_WRONLY|O_TRUNC,0600)))
      {
          perror("open s_file_faile\n");
          return -1;
      }
      while(r_count=read(fd_s,buf,N))
      {
          if(-1==r_count)
         	 {
				perror("open s_file_faile\n");
         		 return -1;
			}
			if(-1==write(fd_d,buf,r_count))
			{
				perror("open s_file_faile\n");
      		    return -1;
			}
	 }
	 close(fd_s);
	 close(fd_d);
	 return 0
	 }

int main()
{

		file_copy("read.txt","write.txt");
}

文件定位:off_t lseek(int fd,off_t offset,int whence);

头文件

#include<unistd.h>
#include<sys/types.h>

返回值:成功:返回挡墙读写的位置,失败:返回-1
第一个参数:文件描述符
第二个参数:ofset 相对于基准点的偏移位置
第三个参数:whence基准值

whence基准值
SEEK_SET代表文件起始位置
SEEK_END代表文件结束位置
SEEK_CUR代表文件当前读写位置

文件锁:int fcntl.h(int fd,int cmd,struct *flock)

文件锁只是fcntl的其中一个用法
头文件:

#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>

返回值:成功:0;出错:-1
第一个参数:文件描述符
第二个参数:cmd,对文件描述符代表的文件进行相关操作
第三个参数:一个结构体flocck,决定如何操作文件

cmd对文件描述符代表的文件进行相关操作
F_GETLK检查文件锁状态
F_SETLK设置lock描述的文件锁
F_SETLKW这是F_SETLK的阻塞版本(命名中的w表示等待)
flock一个表示锁属性的结构体
l_type锁定类型
l_start加锁区域在文件中的相对位移量,与whence一起决定加锁的起始位置
ll_whence相对位移量的起点
l_len加锁区域的长度
l_pid具有阻塞当前进程的锁,其持有的进程号存放在l_pid中,仅由F_GETLK返回
l_type锁定类型
F_RDLCK读写锁(共享锁)
F_WRLCK写入锁(排斥锁)
F_UNLCK解锁
ll_whence相对位移量的起点
SEEK_SET当前位置为文件的开头,新位置为偏移量的大小
SEEk_CUR当前位置为文件指针的位置,新位置为当前位置加偏移量
SEEK_END当前位置为文件的结尾,新位置为文件的长度加偏移量的大小

关于文件锁示例:

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
int lock_set(int fd,int type)
{
    struct flock lock;
    lock.l_whence=SEEK_SET;
    lock.l_start=0;
    lock.l_len=0;
    lock.l_type=type;
    lock.l_pid=-1;
    fcntl(fd,F_GETLK,&lock);
    if(lock.l_type!=F_UNLCK)
    {
        if(lock.l_type!=F_UNLCK)
        {
        
            if(lock.l_type==F_RDLCK)
            {
                printf("Read lock already set by %d\n",lock.l_pid);
            
            }    
            else if(lock.l_type==F_WRLCK)
            {
            
                printf("Write lock already set by %d\n",lock.l_pid);
            }
        
        }
    
    }
    //l_type maybe change by F_GETLK*
    lock.l_type=type;
      /*根据不同的typed值进行阻塞式上锁或解锁*/
    if(fcntl(fd,F_SETLKW,&lock)<0)
    {
       printf("Lock failed:type=%d\n",lock.l_type);
       return -1;
    }
    switch(lock.l_type)
    {
        case F_RDLCK:
               {
                printf("Read lock by set %d\n",getpid());           
               }
               break;
        case F_WRLCK:
               {
               printf("Write lock set by %d\n",getpid());
               }
               break;
         case F_UNLCK:
               {
                  printf("Release lock by %d\n",getpid());
                  return 1; 
               }
               break;
    }/*endl switch*/
    return 0;
}

int main()
{
        int fd;
        if((fd=open("fcntl.txt",O_RDWR|O_CREAT,0600))<0)
        {
            perror("faile open fail\n");
        }
        int n=0;
        lock_set(fd,F_WRLCK);
        getchar();
        char buf[4]="yyyy";
        n=write(fd,buf,4);
        lock_set(fd,F_UNLCK);           
        getchar();
        close(fd);
        return 0;

}

我们打开2个终端
在2个中端上分别运行结果:./aout
第一个终端:Write lock set by 11606

			Release lock by 11606
			
第二个终端:Write lock already set by 11606
			Write lock set by 11607

			Release lock by 11607

对于读文件锁的例子:只需要在上面例子上修改main()函数

int main()
{
        int fd;
        if((fd=open("fcntl.txt",O_RDWR|O_CREAT,0600))<0)
        {
            perror("faile open fail\n");
        }
        int n=0;
        lock_set(fd,F_RDLCK);
        char buf[4];
        getchar();
        read(fd,buf,4);
        lock_set(fd,F_UNLCK);           
        getchar();
        close(fd);
        return 0;

}

我们打开2个终端
在2个中端上分别运行结果:./aout
第一个终端:Read lock set by 11606

			Release lock by 11606
			
第二个终端:
			Read lock set by 11607

			Release lock by 11607
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值