前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux内核编程--文件描述符

Linux内核编程--文件描述符

作者头像
Coder-ZZ
发布2022-05-09 21:40:55
2.8K0
发布2022-05-09 21:40:55
举报
文章被收录于专栏:C/C++进阶专栏

在Linux操作系统中,一切皆是文件—— "Everything is a file"。

如果要在Linux系统中编写操作文件的代码,需要借助文件描述符。

文件的索引——文件描述符(file descriptor):

文件描述符是一个非负整数,当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。文件描述符就是内核为了高效管理已被打开的文件所创建的索引值。 文件描述符指向内核为每一个进程维护的打开文件记录表。当要处理文件时,将文件对应的文件描述符作为参数传给函数。

文件描述符在用户进程内核之间的映射关系:

Linux中查看文件描述符的指令:lsof(list open files)

lsof可以打开的文件:

代码语言:javascript
复制
普通文件
目录
网络文件系统的文件
字符或设备文件
(函数)共享库
管道,命名管道
符号链接
网络文件(例如:NFS file、socket)
其它类型的文件等

样例:

代码语言:javascript
复制
lsof -u username   --列出某个用户打开的文件信息
lsof -c mysql      --列出某个程序进程所打开的文件信息, 也可以用:lsof | grep mysql
lsof -p 11968      --通过某个进程号显示该进程打开的文件
lsof -i            --列出所有的网络连接
lsof -i tcp        --列出所有tcp 网络连接信息

*Linux中出现“ Too many open files”报错,如何解决:

代码语言:javascript
复制
将最大打开文件数增加到8000:
编辑/etc/security/limit.conf文件
将nofiles对应的值改为8000
如果只希望更改在当前会话中生效,用"ulimit -n 8000"

POSIX 标准提供的默认文件描述符:

操作系统提供的四种常见的I/O文件操作函数 (fd表示“文件描述符”):

代码语言:javascript
复制
fd = open(pathname, flags, mode)
numread = read(fd, buffer, count)
numwritten = write(fd, buffer, count)
status = close(fd)

实现I/O操作的主要函数:

1.open()

open() 函数用于打开或者创建文件。其在打开或者创建文件时可以指定文件的属性及用户的权限等各种参数。要使用 open() 函数,需要包含 #include <sys/stat.h>#include <fcntl.h> 这两个头文件。下面是函数的说明:

代码语言:javascript
复制
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *path, int openflag, [mode_t mode]);
args:
    const char *path: 文件路径,可以是绝对,也可以是相对路径
    int openflag    : 文件打开的方式
                        - O_RDONLY 只读打开
                        - O_WRONLY 只写打开
                        - O_RDWR   可读可写打开
                        以上3种必选一个,以下4种可以任意选择
                        - O_APPEND 追加打开,所写数据附加到文件末
                        - O_CREAT  若此文件不存在则创建它
                        - O_EXCL   若文件存在则报错返回
                        - O_TRUNC  如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断为0字节
    [mode_t mode]   : 文件权限,只有在创建文件时需要使用
return:
    文件描述符,非负整数是成功,-1是失败

常见的openflag:

2.write()

write() 向打开的文件写入数据,要使用这个函数,需要包含 #include <unistd.h>

代码语言:javascript
复制
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbyte);
args:
    int fd         : 文件描述符
    const void *buf: 写入数据在内存空间存储的地址
    size_t nbyte   : 期待写入数据的最大字节数
return:
    文件实际写入的字节数,非负整数是成功,-1是失败(磁盘已满或者超出该文件的长度等)

3.read()

读文件函数 read() ,需要包含 #include <unistd.h>

代码语言:javascript
复制
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbyte);
args:
    int fd      : 文件描述符
    void *buf   : 读取数据在内存空间存储的地址
    size_t nbyte: 期待读取数据的最大字节数
return:
    文件实际读取的字节数,非负整数是成功,-1是失败

4.lseek()

在每个打开的文件中都有一个文件的偏移量,文件的偏移量会根据文件的读写而改变位置。我们可以通过 lseek() 函数来调整文件的偏移量。默认情况下,新打开文件的文件偏移量在文件的开始

代码语言:javascript
复制
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
args:
    int fd      : 文件描述符
    off_t offset: 文件偏移量移动的距离
    int whence  : 文件偏移量的基址
                      SEEK_SET 文件开始处
                      SEEK_CUR 文件当前位置
                      SEEK_END 文件结束处
return:
    当前文件指针的位置,非负整数是成功,-1是失败

whence在文件中的位置如图所示:

当文件不再被使用时,可以调用 close(int fd) 函数来关闭被打开的文件。

代码样例:

Demo1:

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

/**
* This is a simple example for using open(), write(), read(), lseek() and close().
*/
int main(int argc, char *argv[])
{
    int fd;
    ssize_t wr_size, rd_size;
    char buffer[128];
    char string_1[30], string_2[30] = "This is the second line!\n";
    char *path = "./file_io.log";
    
    fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 511);
    if (fd < 0) {
        printf("File create fail...\n");
        return -1;
    } else {
        printf("File create success...\n");
    }   
    /* write the first line to file_io.log */
    strcpy(string_1, "This is a demo for file_io!\n");
    wr_size = write(fd, string_1, strlen(string_1));
    if (wr_size < 0) {
        printf("File write 1 fail...\n");
        printf("wr_size = %d\n", wr_size);
        return -1;
    } else {
        printf("File write 1 success...\n");
        printf("wr_size = %d\n", wr_size);
    }    
    /* write the second line to file_io.log
     * in this case, we only write 10 bytes data from string_2 to file.
     */
    wr_size = write(fd, string_2, 10);
    /* add "\0"(not '\0'!!) to the end of the second line */
    wr_size = write(fd, "\0", 1);
    if (wr_size < 0) {
        printf("File write 2 fail...\n");
        printf("wr_size = %d\n", wr_size);
        return -1;
    } else {
        printf("File write 2 success...\n");
        printf("wr_size = %d\n", wr_size);
    }
    
    /* decrease current file offset by 20 bytes */
    lseek(fd, -20, SEEK_CUR);


    rd_size = read(fd, buffer, 100);
    if (rd_size < 0) {
        printf("File read_1 fail...\n");
        printf("rd_size = %d\n", rd_size);
        return -1;
    } else {
        printf("File read_1 success...\n");
        printf("rd_size = %d,\nbuffer = %s\n", rd_size, buffer);
    }
    close(fd);
    return 0;
}

Demo2:模拟文件拷贝

代码语言:javascript
复制
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[])
{
    int inputFd, outputFd, openFlags;
    mode_t filePerms;
    ssize_t numRead;
    char buf[BUF_SIZE];
    if (argc != 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s old-file new-file\n", argv[0]);
    /* Open input and output files */
    inputFd = open(argv[1], O_RDONLY);
    if (inputFd == -1)
        errExit("opening file %s", argv[1]);
    openFlags = O_CREAT | O_WRONLY | O_TRUNC;
    filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |S_IROTH | S_IWOTH; /* rw-rw-rw- */
    
    outputFd = open(argv[2], openFlags, filePerms);
    if (outputFd == -1)
        errExit("opening file %s", argv[2]);
    /* Transfer data until we encounter end of input or an error */
    while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
        if (write(outputFd, buf, numRead) != numRead)
            fatal("write() returned error or partial write occurred");
    if (numRead == -1)
        errExit("read");
    if (close(inputFd) == -1)
        errExit("close input");
    if (close(outputFd) == -1)
        errExit("close output");
    exit(EXIT_SUCCESS);
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员与背包客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档