欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux 使用系统调用进行文件读写

程序员文章站 2022-06-24 20:34:09
...

总结《Unix/Linux系统编程》中关于使用系统调用进行文件读写的部分
涉及opendir、readdir、readlink、open、close、read、write、lseek系统调用

1. 打开,读取目录文件

目录也是一个文件,但是与普通文件不同,目录文件通过opendirreaddir库函数进行打开与读取

1.1 opendir库函数

根据目录名打开目录文件,返回一个指向该目录流的指针,该流被定位在目录的第一个条目上。目录中的每一个条目都指向该目录中的一个文件,目录中还有两个特殊的目录条目即".“和”.."分别是该目录文件和上一级目录文件。失败返回NULL

DIR *opendir(const char * name)

1.2 readdir库函数

readdir()函数返回一个指向dirent结构的指针,该结构代表了目录流中的下一个目录条目。当到达目录流的末端或发生错误时,返回NULL

struct dirent * readdir(DIR * dirp)

dirent结构体

           struct dirent {
               ino_t          d_ino;       /* inode number */
               off_t          d_off;       /* not an offset */
               unsigned short d_reclen;    /* 该条目长度 */
               unsigned char  d_type;      /* 文件类型 */
               char           d_name[256]; /* 文件名,最长255个字符 */
           };

1.3 目录文件示例

打开目录文件并打印出该目录中所有条目信息

test1.c

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

int main(){
    DIR* dir = opendir("/home/xtark/wudi/210525");
    struct dirent *d;
    while(d = readdir(dir)){
        printf("the inode number is : %ld\n",d->d_ino);
        printf("the d_off is %ld\n",d->d_off);
        printf("the lengh of this record is %d\n",d->d_reclen);
        printf("the type of this file is %d\n",d->d_type);
        printf("the file name is %s\n\n\n",d->d_name);
    }
}

运行结果:可以看出有两个目录条目分别是".“和”.."分别指向该目录文件与上一级目录文件

[email protected]-vmpc:~/桌面/linux_study/section8$ gcc test1.c
[email protected]-vmpc:~/桌面/linux_study/section8$ ./a.out 
the inode number is : 1045496
the d_off is : 2240378328779100918
the lengh of this record is : 32
the type of this file is : 8
the file name is : test.o


the inode number is : 1045454
the d_off is : 2801854051817173045
the lengh of this record is : 24
the type of this file is : 4
the file name is : .


the inode number is : 1045439
the d_off is : 2838125603082282148
the lengh of this record is : 24
the type of this file is : 4
the file name is : ..


the inode number is : 1045490
the d_off is : 3113460208815849764
the lengh of this record is : 32
the type of this file is : 8
the file name is : test.h

2. 读取软链接文件readlink

Linux中open系统调用遵循符号链接,即如果open一个软链接文件,那么实际打开的是该软链接文件引用的目标文件而非软链接文件本身

如果要打开软链接文件本身,需要使用readlink系统调用

该系统调用根据文件名打开软链接文件本身,并将文件内容复制len个字节到buf中,并返回实际复制字节数

ssize_t readlink(const char * path, char * buf, size_t len)

示例:

创建log.txt及它的软链接文件symlinkFile.txt(退回到上一级目录创建该软链接文件),然后编写test2.c代码

#include <fcntl.h>           
#include <unistd.h>
#include <stdio.h>
int main(){
    char buf[1024];
    int num = readlink("../symlinkFile.txt",buf,sizeof(buf)-1);
    buf[num] = 0;
    printf("%s\n",buf);
}

运行结果:可见软链接文件的内容仅仅是其链接的目标文件的路径。这也是为什么读取软链接文件时,系统会自动将访问者导向其指向的目标文件

[email protected]:~/桌面/linux_study/section8$ gcc test2.c 
[email protected]:~/桌面/linux_study/section8$ ./a.out 
section8/log.txt

3. open-close-read-write-lseek系统调用

3.1 close系统调用

close()函数关闭文件描述符(使之成为未使用的文件描述符,后面其他文件可以使用该fd)并释放相应的内核资源,成功返回0,出错返回-1

int close(int fd);

参数fd是要关闭的文件描述符。需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

3.2 open系统调用

open()函数会打开一个文件,并使用最小的未使用的文件描述符数值作为文件描述符(因此一般是从3开始,然后4,5,6一直下去)成功返回文件描述符,失败返回-1

int open(const char *pathname, int flags,mode_t mode);
  • 参数path是要打开或者要创建的文件路径

  • 参数flags用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)构成:

    flags必须包含下列访问模式中的一种O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)

    上述的标志位可以与其他标志进行按位或|组合:O_CREAT(如果文件不存在则创建)、O_APPEND(追加)、O_TRUNC(若文件存在,则长度被截为0)、O_CLOEXEC(在进程执行exec系统调用时关闭此打开的文件描述符)

    除此之外还有很多模式,这里只列举了常用的部分。

  • 第三个参数mode仅当创建新文件时才使用,用于指定文件的访问权限位,如八进制数字如0777,表示文件所有者、该文件用户组、其他用户都有可读可写可执行权限。

3.3 read系统调用

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

第一个参数fd是文件描述符,第二个参数buf是缓存区,第三个参数count是读取字节数。即为从fd代指的文件中读取count个字节到buf缓冲区。

返回读取的字节数,-1表示读取失败,如果在调read之前已到达文件末尾,则这次read返回0。

示例:log.txt文件中有13个字节的内容,main.c文件分三次读取log.txt,每次读取8个字节并打印输出

log.txt

1234567890abc

main.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
    int fd = open("log.txt",O_RDONLY);//打开文件并返回文件描述符
    char buf[20];           //设置缓存区保存读取到的数据
    int num = 0;            //记录读取到的字节数
    num = read(fd,buf,8);   //读取8个字节
    buf[num] = '\0';        //字符串结尾
    printf("%d %s\n",num,buf);
    num = read(fd,buf,8);   //读取8个字节
    buf[num] = '\0';        //字符串结尾
    printf("%d %s\n",num,buf);
    num = read(fd,buf,8);   //读取8个字节
    buf[num] = '\0';        //字符串结尾
    printf("%d %s\n",num,buf);
}

运行结果

[email protected]:~/桌面/linux_study/section3/pip test$ gcc main.c 
[email protected]:~/桌面/linux_study/section3/pip test$ ./a.out 
8 12345678
5 90abc
0

可见第一次成功读取了8个字节,第二次虽然想读取8个字节然而只读取了5个字节,第三次由于读写指针指向文件尾,因此读取到了0个字节。

3.4 write系统调用

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

第一个参数fd是文件描述符,第二个参数buf是缓存区,第三个参数count是写入字节数。即将buf中的count字节个数据写入fd代指的文件中。

返回写入的字节数,-1表示写入失败

3.5 lseek系统调用

该系统调用用于改变文件读写指针位置。将文件读写指针相对whence移动offset个字节

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

第一个参数是文件描述符fd

第二个参数是偏移量,大于0向文件尾偏移,小于0向文件头偏移

第三个参数是从哪里开始偏移,有三个值:SEEK_SET(从文件开头)、SEEK_CUR(从当前位置)、SEEK_END(从文件末尾)

如果成功返回从文件开头的偏移位置(以字节为单位)。 出错返回-1 并设置errno