文件I/O(一)

本文转载自实验楼:文件I/O(一)

Linux系统调用

Linux系统调用(system call)是指操作系统提供给用户程序的一组“特殊接口”,用户程序可以通过这组“特殊”接口来获得操作系统提供的特殊服务。
为了更好的保护内核空间,将程序的运行空间分为内核空间和用户空间,他们运行在不同的级别上,在逻辑上是相互隔离的。在Linux中,用户程序不能直接访问内核提供的服务,必须通过系统调用来使用内核提供的服务。
Linux中的用户编程接口(API)遵循了UNIX中最流行的应用编程界面标准——POSIX。这些系统调用编程接口主要是通过C库(libc)实现的。

文件描述符

对内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当写一个文件时,用open或create返回的文件描述符标识该文件,将其作为参数传送给read或write。
在POSIX应用程序中,整数0、1、2应被代换成符号常数:

  • STDIN_FILENO(标准输入,默认是键盘)
  • STDOUT_FILENO(标准输出,默认是屏幕)
  • STDERR_FILENO(标准错误输出,默认是屏幕)

这些常数都定义在头文件<unistd.h>中,文件描述符的范围是0~OPEN_MAX。早期的UNIX版本采用的上限值是19(允许每个进程打开20个文件), 现在很多系统则将其增加至256。
可用的文件I\O函数很多,包括:打开文件,读文件,写文件等。大多数Linux文件I\O只需要用到5个函数:open,read,write,lseek以及close。

基本API

open

需要包含的头文件:

  • <sys/types.h>
  • <sys/stat.h>
  • <fcntl.h>

函数原型:

int open(const str * pathname, int oflag, [..., mode_t mode])

功能:打开文件
返回值:成功则返回文件描述符,出错返回-1
参数:

  • pathname: 打开或创建的文件的全路径名
  • oflag:可用来说明此函数的多个选择项, 详见后。
  • mode:对于open函数而言,仅当创建新文件时才使用第三个参数,表示新建文件的权限设置。

详解oflag参数: oflag 参数由O_RDONLY(只读打开)、O_WRONLY(只写打开)、O_RDWR(读写打开)中的一个于下列一个或多个常数 O_APPEND: 追加到文件尾 O_CREAT: 若文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明新文件的访问权限 O_EXCL: 如果同时指定O_CREAT,而该文件又是存在的,报错;也可以测试一个文件是否存在,不存在则创建。 O_TRUNC: 如果次文件存在,而且为读写或只写成功打开,则将其长度截短为0 O_SYNC: 使每次write都等到物理I\O操作完成

用open创建一个文件: open.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FILE_PATH   "./test.txt"

int main(void)
{
    int fd;
    if ((fd = open(FILE_PATH, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
        printf("open error\n");
        exit(-1);
    } else {
        printf("open success\n");
    }
    return 0;
}

如果当前目录下以存在test.txt,屏幕上就会打印“open error”;不存在则创建该文件,并打印“open success”。

read

需要包含的头文件:<unistd.h>
函数原型:

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

功能:从打开的文件中读取数据。
返回值:实际读到的字节数;已读到文件尾返回0,出错的话返回-1,ssize_t是系统头文件中用typedef定义的数据类型相当于signed int
参数:

  • fd:要读取的文件的描述符
  • buf:得到的数据在内存中的位置的首地址
  • count:期望本次能读取到的最大字节数。size_t是系统头文件中用typedef定义的数据类型,相当于unsigned int
write

需要包含的头文件:<unistd.h>
函数原型:

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

功能:向打开的文件写数据
返回值:写入成功返回实际写入的字节数,出错返回-1,返回-1的常见原因是:磁盘空间已满,超过了一个给定进程的文件长度

参数:

  • fd:要写入文件的文件描述符
  • buf:要写入文件的数据在内存中存放位置的首地址
  • count:期望写入的数据的最大字节数

read和write使用范例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    char buf[100];
    int num = 0;

    // 获取键盘输入,还记得POSIX的文件描述符吗?
    if ((num = read(STDIN_FILENO, buf, 10)) == -1) {
        printf ("read error");
        error(-1);
    } else {
    // 将键盘输入又输出到屏幕上
        write(STDOUT_FILENO, buf, num);
    }

    return 0;
}
close

需要包含的头文件:unistd.h
函数原型:

int close(int filedes)

功能:关闭一个打开的文件
参数:需要关闭文件的文件描述符
当一个进程终止的时候,它所有的打开文件都是由内核自动关闭。很多程序都使用这一功能而不显式地调用close关闭一个已打开的文件。 但是,作为一名优秀的程序员,应该显式的调用close来关闭已不再使用的文件。

lseek

每个打开的文件都有一个“当前文件偏移量”,是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作都是从当前文件偏移量处开始,并使偏移量增加所读或写的字节数。默认情况下,你打开一个文件时(open),除非指定O_APPEND参数,不然位移量被设为0。
需要包含的头文件:

  • sys/types.h
  • unistd.h

函数原型:

off_t lseek(int filesdes, off_t offset, int whence)

功能:设置文件内容读写位置
返回值:成功返回新的文件位移,出错返回-1;同样off_t是系统头文件定义的数据类型,相当于signed int 参数:
whence是SEEK_SET, 那么该文件的位移量设置为据文件开始处offset个字节
whence是SEEK_CUR, 那么该文件的位移量设置为当前值加offset。offset可为正或负
whence是SEEK_END, 那么该文件的位移量设置为文件长度加offset。offset可为正或负

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char * argv[])
{
    int fd;
    char buf[100];
    if ((fd = open(argv[1], O_RDONLY)) < 0) {
        perror("open");
        exit(-1);
    }
    read(fd, buf, 1);
    write(STDOUT_FILENO, buf, 1);
    lseek(fd, 2, SEEK_CUR);

    read(fd, buf, 1);
    write(STDOUT_FILENO, buf, 1);
    lseek(fd, -1, SEEK_END);

    read(fd, buf, 1);
    write(STDOUT_FILENO, buf, 1);
    lseek(fd, 0, SEEK_SET);

    read(fd, buf, 1);
    write(STDOUT_FILENO, buf, 1);
    close(fd);
    printf("\n");

    return 0;
}
select

之前的read函数可以监控一个文件描述符(eg:键盘)是否有输入,当键盘没有输入,read将会阻塞,直到用户从键盘输入为止。用相同的方法可以监控鼠标是否有输入。但想同时监控鼠标和键盘是否有输入,这个方法就不行的了。

// /dev/input/mice 是鼠标的设备文件
fd = open("/dev/input/mice", O_RDONLY);
read(0, buf, 100);
read(fd, buf, 100);

在上面的程序中,当read键盘的时候,若无键盘输入则程序阻塞在第2行,此时即使鼠标有输入,程序也没有机会执行第3行获得鼠标的输入。这种情况就需要select同时监控多个文件描述符。
需要包含的头文件:sys/select.h
函数原型:

int select(int maxfd, fd_set \* readset, fd_set \* writeset, fd_set \* exceptset, const struct timeval \* timeout)

返回值:失败返回-1,成功返回readset,writeset,exceptset中所有,有指定变化的文件描述符的数目(若超时返回0)
参数:

  • maxfd:要检测的描述符个数, 因此值应为最大描述符+1
  • readset:被监控是否有输入的文件描述符集。不监控时,设为NULL
  • writeset:被监控是否可以输入的文件描述符集。不监控时,设为NULL
  • exceptset:被监控是否有错误产生的文件描述符集。不监控时,设为NULL
  • timeval:监控超时时间。设置为NULL表示一直阻塞到有文件描述符被监控到有指定变化。

Tips: readset,writeset,exceptset这三个描述符集指针均是值—结果参数,调用的时候,被监控描述符相应位需要置1;返回时,未就绪的描数字相应位会被清0,而就绪的会被置1。 下面的系统定义的宏,和select配套使用
FD_ZERO(&rset):将文件描述符集rset的所有位清0
FD_SET(4, &reset):设置文件描述符集rset的bit 4
FD_CLR(fileno(stdin), &rset):将文件描述符集rset的bit 0清0
FD_ISSET(socketfd, &rset):若文件描述符集rset中的socketfd位置1

#include <stdio.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>

#define MAXNUM      100
#define OPEN_DEV    "/dev/input/mice"

int main(void)
{
    fd_set rfds;
    struct timeval tv;
    int retval, fd;
    char buf[MAXNUM];

    fd = open(OPEN_DEV, O_RDONLY);
    while (1) {
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        FD_SET(fd, &rfds);
        tv.tv_sec = 5;
        tv.tv_usec = 0;

        retval = select(fd+1, &rfds, NULL, NULL, &tv);
        if (retval < 0)
            printf ("error\n");
        if (retval == 0)
            printf ("No data within 5 seconds\n");
        if (retval > 0) {
            if (FD_ISSET(0, &rfds)) {
                printf ("Data is available from keyboard now\n");
                read(0, buf, MAXNUM);
            }
            if (FD_ISSET(fd, &rfds)) {
                printf ("Data is available from mouse now\n");
                read(fd, buf, MAXNUM);
            }
        }
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    数据革命阅读 12,112评论 2 34
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 1.文件描述符 所有执行I/O操作的系统调用都以文件描述符(一个非负整数)来指代打开的文件。文件描述符用以表示所有...
    666真666阅读 1,058评论 0 2
  • 文件操作 (Linux文件操作)) [文件|目录] Linux文件操作:为了对文件和目录进程处理,你需要用到系统...
    JamesPeng阅读 1,441评论 1 5
  • 一、什么是概率论 概率论是研究随机性或不确定性等现象的数学。 更精确地说,概率论是用来模拟实验在同一环境下会产生不...
    孤独的时候写点字吧阅读 417评论 0 4