文件I/O实践(1) --基础API

什么是I/O

输入/输出是内存和外设之间拷贝数据的过程:

设备->内存: 输入操作

内存->设备: 输出操作

高级I/O: ANSI C提供的标准I/O库函数成为高级I/O, 也称为带缓冲的I/O;

低级I/O: Linux 提供的系统调用, 通常也称为不带缓冲的I/O;

文件描述符

对于Linux内核而言, 所有的文件或设备都对应一个文件描述符(Linux的设计哲学: 一切皆文件), 这样可以简化系统编程的复杂程度;

当打开/创建一个文件的时候, 内核向进程返回一个文件描述符(是一个非负整数). 后续对文件的操作只需通过该文件描述符即可进行, 内核记录有关这个打开文件的信息;

一个进程启动时, 默认已经打开了3个文件, 标准输入(0, STDIN_FILENO), 标准输出(1, STDOUT_FILENO), 标准错误输出(2, STDERR_FILENO), 这些常量定义在unistd.h头文件中;

其中, 文件描述符基本上是与文件描述指针(FILE*)一一对应的, 如文件描述符0,1,2 对应 stdin, stdout, stderr;

文件指针与文件描述符的转换

fileno: 将文件指针转换成文件描述符

int fileno(FILE *stream);

fdopen: 将文件描述符转换成文件指针

FILE *fdopen(int fd, const char *mode);

//示例
int main()
{
    cout << "fileno(stdin) = " << fileno(stdin) << endl;
    cout << "fileno(stdout) = " << fileno(stdout) << endl;
    cout << "fileno(stderr) = " << fileno(stderr) << endl;
    return 0;
}

文件I/O API

1.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);

参数:

pathname:  文件名, 可以包含[绝对/相对]路径名;

flags: 文件打开模式;

mode: 用来指定对文件所有者, 文件用户组以及系统中的其他用户的访问权限;

注意: newMode = mode & ~umask

flags常用值

//示例1
int main()
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
    {
        cerr << "file open error, errno = " << errno <<
             "\nstrerror: " << strerror(errno) << endl;
        perror("perror");
        exit(EXIT_FAILURE);
    }
    cout << "file open success" << endl;
}
//示例2
inline void err_exit(std::string message)
{
    perror(message.c_str());
    exit(EXIT_FAILURE);
}

int main()
{
    umask(0000);
    int fd = open("test.txt", O_RDWR|O_CREAT|O_EXCL, 0666);
    if (fd == -1)
        err_exit("file open error");
    else
        cout << "file descriptor = " << fd << endl;
}

[附]

(1). umask API

//改变umask值

mode_t umask(mode_t mask);

(2). ulimit -a

查看系统中的各种限制;

其中-n: 查看一个进程所能够打开的最大文件数

(3). cat /proc/sys/fs/file-max 

查看一个系统能够支持的最大打开文件数(该数与内存大小有关)

2.close

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

关闭文件描述符, 使得文件描述符得以重新利用

3.read

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

返回值:

错误: -1

到达文件尾: 0

成功: 返回从文件复制到规定缓冲区的字节数

4.wirte

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

返回值:

错误: -1

什么都没做: 0

成功: 返回成功写入文件的字节数

注意:

write返回大于0时, 并不代表buf的内容已经写入到磁盘上的文件中了, 其仅仅代表buf中的数据已经copy到相应的内核缓冲区了. 要实现将缓冲区的内容真正”冲洗”到磁盘上的文件, 需要调用fsync函数;

int fsync(int fd);

其将内核缓冲区中尚未写入磁盘的内容同步到文件系统中;

其实在open调用的时候也可以指定同步选项:O_SYNC  O_SYNC The file is opened for synchronous I/O.   Any  write(2)s  on  the  resulting  file  descriptor will block the calling process until the data has been physically written to the underlying hardware.

write会等到将buf的内容真正的写入到磁盘才真正返回;

//示例: 带有O_SYNC选项
int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        cerr << "Usage : " << argv[0] << " src dest" << endl;
        exit(EXIT_FAILURE);
    }

    int infd = open(argv[1], O_RDONLY);
    if (infd == -1)
        err_exit("file O_RDONLY error");
    int outfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666);
    if (outfd == -1)
        err_exit("file O_WRONLY error");

    char buf[1024];
    int readBytes, writeBytes;
    while ((readBytes = read(infd, buf, sizeof(buf))) > 0)
    {
        writeBytes = write(outfd, buf, readBytes);
        cout << "readBytes = " << readBytes
             << ", writeBytes = " << writeBytes << endl;
    }
}

文件的随机读写

5.lseek

对应于C库函数中的fseek, 通过指定相对于当前位置, 末尾位置或开始位置的字节数来重定位currp:

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

返回值: 新的文件偏移值;

Whence取值:

SEEK_SET

The offset is set to offset bytes.

SEEK_CUR

The offset is set to its current location plus offset bytes.

SEEK_END

The offset is set to the size of the file plus offset bytes.

//示例1
int main(int argc, char *argv[])
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
        err_exit("open error");
    char buf[1024] = {0};
    int readBytes = read(fd, buf, 5);
    cout << "readBytes = " << readBytes << ", buf: " << buf << endl;
    int seekCount = lseek(fd, 0, SEEK_CUR);
    cout << "current offset = " << seekCount << endl;
}
//示例2: 产生空洞文件
int main(int argc, char *argv[])
{
    int fd = open("hole.txt", O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666);
    if (fd == -1)
        err_exit("open error");

    if (write(fd, "ABCDE", 5) == -1)
        err_exit("first write error");
    //创建一个1G的文件
    if (lseek(fd, 1024*1024*1024, SEEK_CUR) == -1)
        err_exit("lseek error");
    if (write(fd, "Hello", 5) == -1)
        err_exit("second write error");
    close(fd);
}

[附]

-查看hole.txt文件

od -c hole.txt

cat -A hole.txt

-查看该文件大小

du -h hole.txt

du -b hole.txt

du -k hole.txt

du -m hole.txt

目录访问

6.opendir

       #include <sys/types.h>
       #include <dirent.h>
       DIR *opendir(const char *name);

返回值:

成功: 返回目录指针;

失败: 返回NULL;

7.readdir

struct dirent *readdir(DIR *dirp);

返回值:

成功: 返回一个指向dirent结构的指针, 它包含指定目录的下一个连接的细节;

没有更多连接时, 返回0;

struct dirent
{
    ino_t          d_ino;       /* inode number */
    off_t          d_off;       /* not an offset; see NOTES */
    unsigned short d_reclen;    /* length of this record */
    unsigned char  d_type;      /* type of file; not supported
                                         by all filesystem types */
    char           d_name[256]; /* filename */
};

8.closedir: 关闭目录   

int closedir(DIR *dirp);
//示例: 简单的ls程序
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        cerr << "Usage : " << argv[0] << " <directory>" << endl;
        exit(EXIT_FAILURE);
    }

    DIR *dir = opendir(argv[1]);
    if (dir == NULL)
        err_exit("opendir error");

    struct dirent *ent;
    while ((ent = readdir(dir)) != NULL)
    {
        //过滤掉隐藏文件
        if (ent->d_name[0] == ‘.‘)
            continue;
        cout << ent->d_name << "\ti-node: " << ent->d_ino
             << ", length: " << ent->d_reclen << endl;
    }
    closedir(dir);
}

9.mkdir

int mkdir(const char *pathname, mode_t mode);

10.rmdir: 删除空目录

int rmdir(const char *pathname);

11. Chmod, fchmod更改权限

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

12.chown,fchown更改文件所有者/所属组

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);

时间: 2024-10-15 09:24:57

文件I/O实践(1) --基础API的相关文章

Linux高性能服务器编程——Linux网络基础API及应用

 Linux网络编程基础API 详细介绍了socket地址意义极其API,在介绍数据读写API部分引入一个有关带外数据发送和接收的程序,最后还介绍了其他一些辅助API. socket地址API 主机字节序和网络字节序 字节序分为大端字节序和小端字节序.小端字节序又被称为主机字节序,大端字节序被称为网络字节序.大端字节序是指一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处.小端字节序则相反. Linux提供如下四个函数完成主机字节序与网络字节序之间的转换: #include

网络编程基础API、属性设置

网络中Socket数据传输是一种特别的I/O,Socket也是一种文件描述符.Socket也具备一个类似于打开文档的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立.数据传输等操作都是通过该Socket实现的. 常用的Socket类型有两种:流式Socket (SOCK_STREAM)和数据报式Socket(SOCK_DGRAM).流式是一种面向连接的Socket,针对于面向连接的TCP服务应用:数据报式Socket是一种无连的Socket,对应于无连接的UDP服

《Linux高性能服务器编程》学习总结(五)——Linux网络编程基础API

第五章      Linux网络编程基础API 对于网络编程,首先要了解的就是字节序的问题,字节序分为主机字节序和网络字节序,主机字节序又称小端字节序,是低字节存放在地地址,而网络字节序又称大端字节序,是低字节放在高地址.当数据在不同的机器上传播时,就需要统一字节顺序以保证不出现错误.在发送数据前,先将需要转变的数据转成网络字节序再发送,接收时先转成主机字节序再处理,要特别注意的是,即使是本机的两个进程通信,也要考虑字节序的问题,比如JAVA的虚拟机就使用大端字节序.使用如下代码可以查看本机的字

轻度破解EXE文件的最近实践

在经历股市多年的大起大落.大赚大赔之后,痛定思痛,深切感到在金融市场拼搏,必须建立健全交易纪律守则,严格执行. 这套完整的纪律守则,就是"交易系统".在许多方面,它与一般的专家系统相近似. 专家系统的最佳开发工具,是Erlang下的Eresye.它的缺点是,其他语言如C.Python对它的调用不方便. 如果主程序是C或Python,一般需将Eresye做成服务器,通过sockets通讯调用,比较麻烦. 在简单实用方面,Prolog是第二佳的开发工具.如果只采用其推理机功能,Visual

Linux基础知识--Linux的文件系统和bash的基础特性

Linux基础知识--linux的文件系统和bash的基础特性 一.Linux文件系统: Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件.目录.软连接及文件保护信息等都存储在其中 linux文件系统遵循FHS(Filesystem Hierarchy Standard,文件系统目录标准),多数Linux版本采用这种文件组织形式.FHS采用了树行组织文件. FHS定义了两层规范,第一层是,/目录下面的各个目录应该要

SaltStack 之 salt-cp 文件复制替换实践

SaltStack 之 salt-cp 文件复制替换实践 一.我们把/srv/salt/files/hosts 文件复制到各从机的的/root下 [[email protected] srv]# salt-cp '*' /srv/salt/files/hosts /root/ {'BackupServer': {'/root/hosts': True}, 'GZ15_ZABBIX': {'/root/hosts': True}, 'GZIFC_ZABBIX': {'/root/hosts': T

第5章 LINUX文件及目录管理命令基础

第5章 LINUX文件及目录管理命令基础5.1 操作LINUX必知必会基础知识5.1.1 Linux命令行组成结构root @ oldboy ~ # :当前用户名 分隔符 主机名 当前用户登陆后的位置,这是个变量 提示符 5.1.2 基本的Linux命令操作语法示例5.1.3 Linux与Windows的目录结构对比5.2 Linux文件及目录核心命令5.2.1 pwd:显示当前所在位置信息"print working directory"-L:逻辑路径:-P:物理路径系统Bash命令

AJAX文件上传实践与分析,带HTML5文件上传API。

对于HTML5已经支持AJAX文件上传了,但如果需要兼容的话还是得用一点小技巧的,HTML5等等介绍,先来看看以前我们是怎么写的. 网上可能会有一些叫AJAX文件上传插件,但在AJAX2.0之前是不可能实现的,因为浏览器的原因,AJAX根本获取不了文件信息,当然这里并不是说就不能文件上传了,只是说在AJAX2.0之前所谓的AJAX文件上传都是假冒的,核心更本没有用AJAX,而是利用iframe实现的,下面我们来看看如何利用iframe实现页面无刷新上传文件. iframe无刷新上传文件版. ht

iptables实践之基础--进阶-实践应用全解析

iptables防火墙可以用于创建过滤(filter)与NAT规则.所有Linux发行版都能使用iptables,因此理解如何配置iptables将会帮助你更有效地管理Linux防火墙.如果你是第一次接触iptables,你会觉得它很复杂,但是一旦你理解iptables的工作原理,你会发现其实它很简单,接下来我们从基础给大家解析iptables防火墙的应用: 第一部分:初识网络安全及防火墙原理 1.Linux网络安全模型 (1).防火墙:主要工作在主机或者网络边缘,对进出报文使用定义的规则进行检