天天看点

Linux文件IO-open,write,read,lseek,close

在linux编程中,经常用到open,write,read,lseek,close这几个函数。这里系统总结一下。

一、函数open

参考:https://linux.die.net/man/2/open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
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);
           

返回值:若成功,返回一个文件描述符fd;若失败,返回-1

函数参数:

  • pathname :打开文件的路径名
  • flags:用来控制打开文件的模式
  • mode:用来设置创建文件的权限(rwx)。当flags中带有 O_CREAT 时,也就是创建新文件时才有效。

函数creat只是用于创建新文件,这样对比看来open的功能更加强大。

由于open函数完全可以替代creat函数,所以现在基本上不用creat函数了。

下面对各个参数进行详细的说明:

flags参数说明:

  • O_RDONLY:只读模式
  • O_WRONLY:只写模式
  • O_RDWR:可读可写模式

flags参数必须包括以上3个参数之一,并且只能选择一个。

还有一些flags参数是可选的,如下:

  • O_APPEND 以追加的方式打开文件
  • O_CREAT 创建一个文件
  • O_EXEC 如果使用了O_CREAT而且文件已经存在,就会发生一个错误
  • O_NOBLOCK 以非阻塞的方式打开一个文件
  • O_TRUNC 如果文件已经存在,则删除文件的内容

我们可以用符号“|”分隔开,例如,flags为O_RDWR | O_APPEND,表示以可读可写的模式打开文件并且追加到文件后面。

如果flags中使用了O_CREAT标志,则调用open函数的时候需要加上打开文件的模式,设置文件的用户权限。也就是说使用O_CREAT标志时,open的原型应该是这样的:

int open(const char *pathname, int flags, mode_t mode);

用过chmod命令的就知道这个怎么用了,可以用八进制数据表示,

其中4:读权限,2:写权限,1:可执行权限,0:无权限。

下面是mode可取的一些值,都是八进制的值,使用这些值的时候需要包含头文件:sys/types.h,sys/stat.h

  • S_IRWXU 00700 用户可以读
  • S_IRUSR 00400 用户可以写
  • S_IWUSR 00200 用户可以执行
  • S_IXUSR 00100 用户可以读、写、
  • S_IRWXG 00070 组可以读
  • S_IRGRP 00040 组可以写
  • S_IWGRP 00020 组可以执行
  • S_IXGRP 00010 组可以读写执行
  • S_IRWXO 00007 其他人可以读
  • S_IROTH 00004 其他人可以写
  • S_IWOTH 00002 其他人可以执行
  • S_IXOTH 00001 其他人可以读、写
  • S_ISUID 04000 设置用户执行ID
  • S_ISGID 02000 设置组的执行ID

实际生成的文件的权限还要受到进程的权限掩码的影响的

如果生成的文件权限跟自己设置的不相符合,请看看是否是root模式下执行的

二、函数write

参考:https://linux.die.net/man/2/write

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
           

返回值:若成功,返回已写的字节数;若出错,返回-1

功能:write 函数把buf指针指向的内容写入count 字节数据到文件描述符为fd的文件 。返回值一般总是等于 count,否则就是出错了。

常见的出错原因是磁盘空间满了或者超过了文件大小限制。如果打开文件时使用了 O_APPEND ,则每次写操作都将数据写入文件末尾。

三、函数read

参考:https://linux.die.net/man/2/read

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
           

返回值:返回所读取的字节数;0(读到EOF);-1(出错)。

功能:read 函数会从文件描述符fd的文件中读取count字节到从buf地址开始的缓存中。

参数:

  • fd: 要读取数据的文件描述符。
  • buf:存放读取的数据缓冲区。
  • count: 一次read操作,读多少数量的字节。

以下几种情况会导致读取到的字节数小于 count :

1. 读取普通文件时,读到文件末尾还不够 count 字节。
2. 从终端设备读取时,一般情况下每次只能读取一行。
3. 从网络读取时,网络缓存可能导致读取的字节数小于 count字节。
4. 读取 pipe 或者 FIFO 时,pipe 或 FIFO 里的字节数可能小于 count 。 
5. 在读取了部分数据时被信号中断。
           

总结:读取到的字节数小于想要读取的字节count,说明了源头没有那么多数据可读。

四、函数lseek

参考:https://linux.die.net/man/2/lseek

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
           

返回值:若成功则返回目前的读写位置,也就是距离文件开头多少个字节。若错误则返回-1。

所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了 O_APPEND 。使用 lseek 函数可以改变文件的 cfo 。

参数说明:

  • Offset:偏移量,每一读写操作所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)。
  • whence为下列其中一种:(SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2)
    1. SEEK_SET 将读写位置指向文件头后再增加offset个位移量。
    2. SEEK_CUR 以目前的读写位置往后增加offset个位移量。
    3. SEEK_END 将读写位置指向文件尾后再增加offset个位移量。

当whence 值为SEEK_CUR 或SEEK_END时,参数offet允许负值的出现。

举几个例子:

  1. 将读写位置移到文件开头:

    lseek(fd,0,SEEK_SET)

  2. 将读写位置移到文件尾:

    lseek(fd,0,SEEK_END)

  3. 取得目前文件位置:

    lseek(fd,0,SEEK_CUR)

五、函数close

参考:https://linux.die.net/man/2/close

#include <unistd.h>
int close(int fd);
           

返回值:若成功,返回0;若出错,返回-1

关闭一个文件时还会释放该进程加在该文件上的所有记录锁。

当一个进程终止时,内核自动关闭它所有的打开文件。很多程序利用了这一功能而不显示地用close关闭打开的文件。

六、示例程序

//open.c
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FILENAME "hello"

int main()
{
    //实际生成的文件的权限还要受到进程的权限掩码的影响的
    //如果生成的文件权限跟自己设置的不相符合,请看看是否是root模式下执行的
    int fd = open(FILENAME,O_RDWR | O_CREAT,);
    if(fd < )
    {
        perror("open");
    }
    printf("open success!\n");
    char write_buf[] = "helloworld!\n";
    if (write(fd,write_buf,sizeof(write_buf)))
    {
        perror("write");
    }
    printf("write success !\n");
    //将文件偏移指针重新指向开头
    if(lseek(fd,,SEEK_SET) < )
    {
        perror("lseek");
    }
    printf("lseek success !\n");    
    char read_buf[] = {};
    if (read(fd , read_buf , ) < )
    {
        perror("read");
    }
    printf("read success !\n");
    printf("read_buf = %s\n",read_buf);
    return ;
}
           

实验结果:

ubuntu:~/test/-IO/open$ gcc open.c -o open
ubuntu:~/test/-IO/open$ ./open
open success!
write: Success
write success !
lseek success !
read success !
read_buf = helloworld!
ubuntu:~/test/-IO/open$ ls
hello  open  open.c
ubuntu:~/test/-IO/open$ cat hello 
helloworld!
           

继续阅读