linux文件编程--(1)open.write.read.lseek.close

open

函数原型

       int open(const char *pathname, int flags);//   两个参数
       int open(const char *pathname, int flags, mode_t mode);//三个参数
       int creat(const char *pathname,mode_t mode);

具体介绍

open 返回的值称为文件描述符(它的作用域是当前进程,在其他进程是无效的)
文件描述符:类似一个索引,用于区分使用open打开的各个文件,在文件打开后进行后续操作都必须通过文件描述符来判断是哪一个文件。当一个文件被打开后,系统会产生一个结构体来存储这个文件的一些信息,而文件描述符会指向这些结构体。
(成功返回一个非负整数,失败则返回-1)

mode:创建模式(可读可写可执行)系统自带四个宏
可执行(1):S_IXUSR
可写(2):S_IWUSRL
可读(4):S_IRUSR
可读、可写、可执行(7):S_IRWXU
//(可读可写2+4 所以写成0600)

open

          int fd;
          fd=open("./file1",O_RDWR);//     "./"表示当前路径
         //  "/home/CLC/SYSTEM_PRO/FILE" 表示绝对路径 (pwd命令获取)
          printf("fd=%d\n",fd);
           return 0;

执行结果 fd=3

creat( create函数也能打开一个文件,如果文件不存在,则创建它。和open一样,creat也在调用成功后返回一个文件描述符,如果失败,则设置errno变量并返回-1.)

 int fd;
          char *buf="text";
          fd=creat("/home/CLC/file2",S_IRWXU);//可读可写可执行
          return 0;

pathname 是一个指向文件路径的指针
flags 文件权限
open(两个参数):(只能3选1)
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 可读可写打开
open(三个参数)
1、O_CREAT:打开文件不存在则创建它,需要说明mode(用于表示新创建文件的权限)。

fd_file1=open("./file1",O_RDWR|O_CREAT,0600);
//fd_file1为open返回的文件描述符,此时若不存在file1,则新建一个file1

2、O_EXCL:当O_CREAT|O_EXCL,而文件已经存在,会出错,返回-1。

fd_file1=open("./file1",O_RDWR|O_CREAT|O_EXCL,0600);
//fd_file1为open返回的文件描述符,此时若已经存在file1,open返回-1

3、O_APPEND:追加写,如果文件已经有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。

fd_file1=open("./file1",O_RDWR|O_APPEND);
//此时写入数据的时候,会从文件已有数据的末尾开始写入

4、O_TRUNC:若文件存在,则长度被截为0,属性不变

fd_file1=open("./file1",O_RDWR|O_TRUNC);
//此时原文件里的信息会先被删除后写入

mode:创建模式(可读可写可执行)
可执行:0100
可写:0200
可读:0400
可读、写:0600
可读、可写、可执行:0700
一定是在flags中使用了O_CREAT标志,mode记录待创建的文件的访问权限

write

函数原型

ssize_t write(int fd,const void *buf,size_f count);

从buf中写入count个字节的数据到文件描述符为fd的文件中。
参数
int fd:fd为文件的文件描述符
const void *buf:buf为须要写入的字节
size_f count:写入了多少长度的字节
返回值
成功:返回写入字节的个数(一个整型数)
失败:返回-1;
示例

          int fd;
          char *buf="happy!!!";
          fd=open("./file1",O_RDWR);

          if(fd==-1){
             printf("Open file failed\n");
             fd=open("./file1",O_RDWR|O_CREAT,0600);
             if(fd>0){
                   printf("creat file success\n");
             }
          }
         
          int n_write=write(fd,buf,strlen(buf));
          //如果把第三个参数改成20,那么输出的n_write的结果也会是20
          printf("fd=%d   n_write=%d \n",fd,n_write);
          close(fd);
          return 0;

运行结果: fd=3 n_write=8

写buf的时候必须指明写多少个,如果buf长度为8,第三个参数指明写入的是20,那么那个文件显示的字符会出现乱码,所以一般使用strlen写入,不用sizeof或者其他超过buf长度的数字。

read

函数原型

read函数只是一个通用的读文件设备的接口。是否阻塞需要由设备的属性和设定所决定。一般来说,读字符终端(键盘输入–标准输入)、网络的socket描述字,管道文件等,这些文件的缺省read都是阻塞的方式。如果是读磁盘上的文件,一般不会是阻塞方式的。但使用锁和fcntl设置取消文件O_NOBLOCK状态,也会产生阻塞的read效果

ssize_t read(int fd,const void *buf,size_f count);

read()从文件描述符fd读取count个字节的数据到从buf开始的缓冲区中。
参数:
int fd:fd为文件的文件描述符
const void *buf:buf为须要读出的字节存放的缓冲区
size_f count:读出了多少长度的字节
返回值:
成功:返回读取字节的个数(一个整型数)
失败:返回-1;
示例

          int fd;
          char *buf="hello!!!";
          fd=open("./file1",O_RDWR);

          if(fd==-1){
             printf("Open file failed\n");
             fd=open("./file1",O_RDWR|O_CREAT,0600);
             if(fd>0){
                   printf("creat file success\n");
             }
          }            //创建或者打开文件
          printf("fd=%d\n",fd);
          int write_n=write(fd,buf,strlen(buf));
          if(write_n!=-1){
              printf("write %d byte to flie\n",write_n);
          }          //写入数据
          char *readBuf;
          readBuf=(char*)malloc(write_n*sizeof(char)+1);
          int read_n =read(fd,readBuf,write_n);//读出数据到readBuf中
          printf("read=%d ,  %s\n",read_n,readBuf);
          close(fd);
          return 0;

fd=3
write 8 byte to flie
read=0,
//为什么会出现读不出来的情况?
//应为打开文件写入的时候 光标是在数据的最末端,而读的时候是从光标处往后面读的,所以这个时候读到的数据是空的
//解决办法:可以在写完之后关闭文件再重新打开,或者用lseek来改变光标位置

lseek

函数原型

       off_t lseek(int fd, off_t offset, int whence);

参数
fd:文件描述符
offset:对于whence的偏移值,负数表示左,正数表示右,0表示原地
whence:固定点的位置

whence有三个固定宏:
SEEK_SET:文件头
SEEK_CUR:文件当前位置
SEEK_END:文件尾巴
应用

 lseek(fd_file1,0,SEEK_SET);//光标移到文件头部
 lseek(fd_file1,0,SEEK_CUR);//光标保留当前位置
 lseek(fd_file1,0,SEEK_END);//光标移到文件尾部
 
 lseek(fd_file1,5,SEEK_SET);//光标移到相对于文件头部右边5个byte
 lseek(fd_file1,5,SEEK_CUR);//光标相对于当前位置右边5个byte
 lseek(fd_file1,-5,SEEK_END);//光标相对于当前位置文件尾部左边5个byte

返回值
成功完成后,lseek()返回相应的偏移量是从文件头到当前光标位置的字节数
发生错误时,返回值(off_t) -1,并设置errno来指示错误。

close

关闭一个打开文件,同时释放该进程加在该文件上所有记录锁。
当一个进程终止时,内核自动关闭她所有的打开文件。很多程序都利用了这一功能而不显式的用close关闭打开打开文件。

int close(int fd) (函数原型)

文件描述符(详解)

1、对于内核而言,所有打开文件的操作都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。
按照惯例,UNIX shell使用文件描述符0与进程的标准输入相结合

程序开始运行时,默认会调用open(“/dev/stdin”, O_RDONLY)将其打开,返回的文件描述符是0
使用0这个文件描述符,可以从键盘输入的数据简单理解就是,/dev/stdin这个文件代表了键盘

read(0, buf, sizeof(buf))实现的是从键盘读取数据到到缓存buf中

文件描述符1标准输出相结合

程序开始运行时,默认open(“/dev/stdout”, O_WRONLY)将其打开,返回的文件描述符是1为什么返回的是1,先打开的是/dev/stdin,把最小的0用了,剩下最小没用的是1,因此返回的肯定是1。
通过1这个文件描述符,可以将数据写(打印)到屏幕上显示简单理解是,
/dev/stdout这个文件代表了显示器。

write(1, buf, strlen(buf))实现将buf中的数据写到屏幕上显示的功能

文件描述符2与标准错误输出相结合

int main()
{
        char readbuf[128];
        read(0,readbuf,5);//键盘输入
        write(1,readbuf,strlen(readbuf));//打印显示出来
        printf("\n");
        return 0;
}

SDINJFILEN0、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0、1、2这个三个数。

2、文件描述符,这个数字在一个进程中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符来区分。
3、文件描述符的作用域就是当前进程,出了这个进程文件描述符就没有意义了。

文件IO理解,fopen和open的区别

系统调用提供的函数如open, close, read, write,ioctl等,需包含头文件unistd.h.以write为例:其操作对象为文件描述符或文件句柄fd(file descriptor),要想写一个文件,必须先以可写权限用open系统调用打开一个文件,获得所打开文件的fd,例如 fd=open(\“/dev/video\”,O_RDWR)。fd是一个整型值,每新打开一个文件,所获得的fd为当前最大fd加1.Linux系统默认分配了3个文件描述符值:0-standard input,1-standard output,2-standard error.

系统调用通常用于底层文件访问,例如在驱动程序中对设备文件的直接访问。

系统调用是操作系统相关的,因此一般没有跨操作系统的可移植性。

系统调用发生在内核空间,因此如果在用户空间的一般应用程序中使用系统调用来进行文件操作,会有用户空间到内核空间切换的开销。事实上,即使在用户空间使用库函数来对文件进行操作,因为文件总是存在于存储介质上,因此不管是读写操作,都是对硬件(存储器)的操作,都必然会引起系统调用。也就是说,库函数对文件的操作实际上是通过系统调用来实现的。例如C库函数fwrite()就是通过write()系统调用来实现的。

这样的话,使用库函数也有系统调用的开销,为什么不直接使用系统调用呢?这是因为,读写文件通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),这时,使用库函数就可以大大减少系统调用的次数。这一结果又缘于缓冲区技术。在用户空间和内核空间,对文件操作都使用了缓冲区,例如用fwrite写文件,都是先将内容写到用户空间缓冲区,当用户空间缓冲区满或者写操作结束时,才将用户缓冲区的内容写到内核缓冲区,同样的道理,当内核缓冲区满或写结束时才将内核缓冲区内容写到文件对应的硬件媒介。

缓冲IO: C语言的fopen fclose fwrite fread fflush fprintf fscanf等
直接IO: Linux的原生API,比如open close fsync read write pwrite pread等
缓冲IO实际上就是用户层还做了一个用户层的缓冲区,直接IO没有用户层缓冲区,但是操作系统内核的缓冲区还是有的。一般就是这两个不一样的地方。性能上有一定差距。

拿Linux来说,内核缓冲区其实也就是内核空间的一段buffer,只是这段buffer一般是以页为单位,Linux会把磁盘上的数据以页为单位缓存在操作系统内核中的内存里,一般来说,一个页是4K大小。(open是发送在内核缓冲的)

缓冲IO的读或写都要经过: 应用程序内存(程序运行时的内存空间)->用户缓冲区->内核缓冲区->磁盘, 其中的数据流转一次,从逻辑上看,有3次数据拷贝。内存中的数据拷贝大家都知道是很耗时的。高性能系统可能会压榨这里

直接IO的读或写都要经过: 应用程序内存->内核缓冲区->磁盘,有2次数据拷贝,性能相对要高。

为什么不直接对块设备直接操作?
块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。(内存到cache的替换也是类似)

原文链接:文件编程步骤

注意事项

在write 和 read 中的“buf” 是一个无类型的指针,存放的是地址。所以说存放的不只是字符串,还可以是结构体,整数等,只需要把地址传过去就可以。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值