Linux文件IO(简易)

Linux中文件分为以下几类:

普通文件,即一般意义上的文件、磁盘文件;
设备文件,代表的是系统中一个具体的设备;
管道文件、FIFO 文件,一种特殊文件,常用于进程间通信;
套接字(socket)文件,主要用在网络通信方面。

 

跟C/C++类似,Linux对文件IO的操作也无非就是打开 关闭 读 写 

我们这里复习一下C语言的文件操作Qt的文件操作

大致是类似的

 

我开始确实是这样以为的。

学了一下才发现,原来open read之流,也只不过九牛一毛 沧海一粟 。。

 

先说一下文件描述符

是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,
内核就向进程返回一个文件描述符;当需要读写文件时, 也需要把文件描述符作为参数传递给相应的函数。

或者叫句柄

类似于你号码牌,通过这个号,就可以找到指定的文件

通过这个操作可以查看文件描述符的取值范围,也就是说一个进程最多只能打开1024个文件

 

 

 

 

然后看一下基本的操作:

首先惯例加入头文件

#include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */
#include <fcntl.h> /* 定义 open,creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */
#include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */
#include <errno.h> /* 与全局变量 errno 相关的定义 */
#include <sys/ioctl.h> /* 定义 ioctl 函数原型 */

 

 

 

打开文件

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

参数:

 

 

 

 

 

 返回值:成功返回文件描述符,失败返回 -1

 

 

 

 

 

关闭文件

int close(int fd);

直接传入刚才获得的文件描述符,则可关闭文件

当一个进程终止时,内核会自动关闭该进程所打开的文件

 

我们看个例子

int main(void) 
{ 
    //文件描述符
    int fd; 
/*调用 open 函数,以可读写的方式打开,注意选项可以用“|”符号连接*/ 
    if((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_WRONLY , 0600 ))<0){ 
        perror("open:"); 
        exit(1); 
    } 
    else{ 
        printf("Open file: hello.c %d\n",fd); 
    } 
    //关闭该文件
    if( close(fd) < 0 ){ 
        perror("close:"); 
        exit(1); 
    } 
    else 
        printf("Close hello.c\n"); 
    
    exit(0); 
}

例子中使用 

O_CREAT | O_TRUNC | O_WRONLY

对照参数表,可知表示:只写方式打开文件,打开时擦除文件内容,如果文件不存在则创建。

 

这里说一下 return exit(0)  和 exit(1)

exit(0):正常运行程序并退出程序;

exit(1):非正常运行导致退出程序;

return():返回函数,若在主函数中,则会退出函数并返回一值。

详细说:

1. return返回函数值,是关键字; exit 是一个函数。

2. return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。
3. return是函数的退出(返回);exit是进程的退出。

4. return是C语言提供的,exit是操作系统提供的(或者函数库中给出的)。

5. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一个状态返回给OS,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出。

6. 非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。

 

 

读取数据

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

先看一下这个返回值类型  ssize_t

/* 在 32 位系统中 */
typedef int ssize_t; /* 32 位有符号整型值 */
typedef unsigned int size_t; /* 32 位无符号整型值*/
/* 在 64 位系统中 */
typedef long int ssize_t; /* 64 位有符号整型值 */
typedef unsigned long int size_t; /* 64 位无符号整型值 */

也就是说,这个东西就是 int

 

几个参数很好理解

fd        文件描述符

buf      存放数据的地址

count  读出数据的大小

 

返回值:如果成功,返回读出的数据字数;如果失败返回 -1 ;读至文件尾 ,返回 0.

 

写入数据

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

和read相同,buf是要写入的数据的地址。

返回值: 成功返回写入数据数,失败返回-1

 

看个例子:

int main(void) 
{ 
    int i,fd,size,len; 
    char *buf="Hello! I'm writing to this file!"; 
    char buf_r[10]; 
    len = strlen(buf); 

/*首先调用 open 函数,并指定相应的权限*/ 
    if((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_RDWR,0666 ))<0){ 
        perror("open:"); 
        exit(1); 
    } 
    else 
        printf("open file:hello.c %d\n",fd); 

/*调用 write 函数,将 buf 中的内容写入到打开的文件中*/ 
    if((size = write( fd, buf, len)) < 0){ 
        perror("write:"); 
        exit(1); 
    } 
    else 
        printf("Write:%s\n",buf); 

/*调用 lsseek 函数将文件指针移到文件起始,并读出文件中的 10 个字节*/ 
    lseek( fd, 0, SEEK_SET ); 
    if((size = read( fd, buf_r, 10))<0){ 
        perror("read:"); 
        exit(1); 
    } 
    else 
        printf("read form file:%s\n",buf_r); 

/*关闭文件*/
    if( close(fd) < 0 ){ 
        perror("close:"); 
        exit(1); 
    } 
    else 
        printf("Close hello.c\n"); 


    exit(0); 
}

 

 

 

这里有个问题,你使用write函数,写入一串数据进文件,不一定真的写入了磁盘等储存设备中

为了确保文件保存了你修改的数据,我们这里使用一个函数用于强制文件数据同步

int fsync(int fd);

成功返回 0 ;失败返回 -1 .

 

 

刚才我们的例子中,是直接从文件头读了数据

那如果你要从随机或者指定的一个地方读数据,就需要重新定位读写位置

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

这里 off_t 也是有符号整型,可以理解为32位的 long  int

 

fd            文件描述符

offset      偏移量  可正可负 分别表示向前向后移动

whence   基点 有三种: 

SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小 
SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量 
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小

返回值:成功返回当前偏移量,失败返回  -1。

 

看个例子:

#include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */
#include <fcntl.h> /* 定义 open,creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */
#include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */
#include <errno.h> /* 与全局变量 errno 相关的定义 */
#include <sys/ioctl.h> /* 定义 ioctl 函数原型 */
#include <string.h> 
#include <stdio.h>
#include <stdlib.h>



int main()
{
    int fd;
    char *readbuf;
    char *writebuf = "111111111111111111";
    char *buf = "000000000";

    if((fd = open("/mnt/hgfs/share/test.c", O_CREAT | O_RDWR,0777 ))<0){ 
        //打开失败
        exit(1); 
    } 
    //写入数据
    write(fd, writebuf, strlen(writebuf));
    //文件数据同步
    fsync(fd);
    system("cat test.c");

    lseek(fd, 6, SEEK_SET);

    //再次写入数据
    write(fd, buf, strlen(buf));
    fsync(fd);

    system("cat test.c");

    
    close(fd);
    return 0;
}

 

 执行hello之后,出现了2次cat test.c

第一次全是111111111111

第二次中间加入了00000000

 

 

还有一些IO操作归不到read write中,所以加入了一个函数 ioctl()

一些硬件操作 寄存器操作也都在该函数中

int ioctl(int fd, int cmd, …);

参数比较简单,分别是文件描述符 和 操作指令

这里的操作指令肯定都是宏定义。

 

 

 

 


 

 

后记:

我在后面试用的时候出现了问题,记录一下。

linux环境下,我代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */
#include <fcntl.h> /* 定义 open,creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */
#include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */
#include <errno.h> /* 与全局变量 errno 相关的定义 */
#include <sys/ioctl.h> /* 定义 ioctl 函数原型 */

int main(int argc, char const *argv[])
{
    int fd;    
    int ret;
    char *buf = "hello hello hello";
    fd = open("/mnt/hgfs/share/txt",O_RDWR|O_CREAT,0x777);
    if(0 > fd)
    {
        perror("open file failed");
    }
  lseek(fd,0,SEEK_END); write(fd,buf,strlen(buf)); fsync(fd); close(fd); exit(
0); }

运行如下:

 

 也就是说,再没有txt这个文件的时候,可以正常创建。

已经创建完成后,在运行该代码就会失败提示错误:拒绝访问

 

然后我使用  ls -la   查看了一下文件的权限

 

 没有写入权限

那么我用 chmod 给他加满权限

则可以正常运行了

 

 

那么问题来了,为什么我创建的 txt 文件会没有写入权限

 

所以我将

 

 改成了

 

 这样就可以运行很多次不会报错了

 

 

中途我 试过 0x700 也不可以

不知道为什么,记住以后这样做,用宏定义。

 

接下来我又:

char *readbuf = NULL;

.....
.....


lseek(fd,5,SEEK_SET);
ret = read(fd,readbuf,5);
printf("%d\n",ret);

想要读出距离文件头5个字节处的数据

结果被报错

 

 

为了解决错误,我尝试了2种方案

1、

char readbuf[5];

2、

char *readbuf = (char *)malloc(sizeof(char*));

运行结果显示,这两种方法都可以正常读取文件。

 

经过排查发现如果我注释掉

fsync(fd);

那么即便在栈区申请空间也不会报错了。

目前这个函数对我来说仍是个迷 - -

解决再更新

 

 

 

lseek(fd,5,SEEK_SET);ret = read(fd,readbuf,5);printf("%d\n",ret);

posted @ 2020-05-20 20:18  祁峰_1024  阅读(302)  评论(0编辑  收藏  举报