Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Linux IO编程

19
Jun
2009

Linux IO编程

By Alex
/ in C,Linux
/ tags IO编程, Linux编程
0 Comments
文件访问

Linux系统中每个运行的进程,具有与之关联的文件描述符,通过这些描述符可以访问打开的文件或者设备。当一个进程打开时,一般会有三个已经打开的文件描述符:

 描述符 说明 
0 代表标准输入
1  代表标准输出 
2 代表标准错误
系统调用

下表是与文件访问有关的系统调用

系统调用  说明 
write

将缓冲区buf的前n个字节写入到文件描述符fildes关联的文件中,返回实际写入的字节数,如果底层设备对数据块长度比较敏感,返回值可能小于n。如果出现错误,返回-1,可以通过全局变量errno访问错误代码。

函数原型: size_t write( int fildes, const void *buf, size_t nbytes ); 

示例代码:

C
1
2
3
#include <unistd.h>
write( 1, "stdout", 6 );
write( 2, "stderr", 6 );
read 

从文件描述符fildes关联的文件中读取n字节数据,并把它们放入缓冲区中,返回实际读入的字节数。如果返回0表示未读入任何数据,已经到达文件结尾;如果返回-1表示出错。

函数原型: size_t read( int fildes, void *buf, size_t nbytes ); 

示例代码:

C
1
2
3
4
5
char buf[128];
int nread;
//阻塞的从标准输入中读取数据
nread = read( 0, buf, 16 );
write( 2, buf, nread );
open

该函数用于创建一个新的文件描述符。该调用会返回一个文件描述符,其它进程即使打开同一个文件,也不会使用相同的描述符。如果两个进程同时写一个文件,那么写入的内容可能会相互覆盖(两个进程读写偏移量独立维护)。

函数原型:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <fcntl.h>
//严格的说, 在遵守POSIX规范的系统上,下面两个头文件不需要包含
#include <sys/types.h>
#include <sys/stat.h>
//oflags可以指定打开模式
//O_RDONLY   只读
//O_WRONLY   只写
//O_RDWR     读写
//oflags还可以按位或以下选项:
//O_APPEND   写入数据追加在文件结尾
//O_TRUNC    设置文件长度为0,丢弃已有内容
//O_CREAT    如果需要,根据mode中给出的模式创建文件
//O_EXCL     与O_CREAT联用,防止其它进程创建同一个文件
//           如果其它进程创建同一文件,该调用将失败
int open(const char *path, int oflags);
//mode在使用O_CREAT选项时有意义,代表文件的模式(权限)
//模式由S_开头的若干常量来表示,使用时按位或
// S_ISUID 04000 文件的 (set user-id on execution)位
// S_ISGID 02000 文件的 (set group-id on execution)位
// S_ISVTX 01000 文件的sticky 位
// S_IRUSR (S_IREAD) 00400 文件所有者具可读取权限
// S_IWUSR (S_IWRITE)00200 文件所有者具可写入权限
// S_IXUSR (S_IEXEC) 00100 文件所有者具可执行权限
// S_IRGRP 00040 用户组具可读取权限
// S_IWGRP 00020 用户组具可写入权限
// S_IXGRP 00010 用户组具可执行权限
// S_IROTH 00004 其他用户具可读取权限
// S_IWOTH 00002 其他用户具可写入权限
// S_IXOTH 00001 其他用户具可执行权限
int open(const char *path, int oflags, mode_t mode);

open调用在成功时返回一个非负整数,作为文件描述符。失败时返回-1并设置全局变量errno。新文件描述符总是使用未用描述符的最小值。该特性被用于重定向:关闭标准输出后,再次调用open,则文件描述符1被重新使用,从而实现重定向。

任何运行中的程序能够打开的文件数量是有限制的,该限制在头文件limits.h中的OPEN_MAX定义,POSIX要求最少可以打开16个,在Linux中,该限制可以在运行时动态调整,因此OPEN_MAX是变量

close

可以终止文件描述符与对应文件的关联,文件描述符被释放,可以重新使用。成功关闭返回0,否则返回-1

lseek

可以对文件描述符的读写指针进行设置,即设置文件的读写位置。返回文件头到指针设置处的字节偏移量,失败时返回-1

函数原型:

C
1
2
3
4
5
6
7
#include <unistd.h>
#include <sys/types.h>
//whence可以为:
// SEEK_SET: 表示偏移量为绝对位置
// SEEK_CUR: 表示偏移量是相对于当前位置的位置
// SEEK_END: 表示偏移量是相对于文件结尾的位置
off_t lseek( int fildes, off_t offset, int whence );
fstat
stat
lstat

返回与打开文件描述符关联的文件的状态信息

函数原型:

C
1
2
3
4
5
6
7
8
9
10
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
//该函数通过描述符定位文件
int fstat( int fildes, struct stat *buf );
//下面两个函数通过文件路径定位
int stat( const char *path, struct stat *buf );
//该函数与stat类似,但是如果文件是符号链接,它会
//返回符号链接本身的信息,而stat返回链接目标的信息
int lstat( const char *path, struct stat *buf );
dup
dup2

用于复制文件描述符,可以让多个文件描述符指向同一文件,从而允许在文件的不同位置进行读写。通过管道在多个进程间进行通信时,这些调用很有用

函数原型:

C
1
2
3
4
5
#include <unistd.h>
//复制并返回新的文件描述符
int dup( int fildes );
//复制文件描述符为fildes2
int dup2( int fildes, int fildes2 );
标准I/O库

标准I/O库(stdio)及其头文件stdio.h为底层I/O系统提供通用的对外接口,现在这个库已经是ANSI C标准库的一部分。标准I/O库提供多种复杂的函数用于格式化输出、扫描输入,还负责设备缓冲的处理。

在标准I/O库中,操控文件的方式与系统调用类似,其中与底层文件描述符对应的是流(Stream),被实现为指向结构体FILE的指针。

在程序启动时,stdin、stdout、stderr这三个文件已经打开。

标准I/O库关于文件读写API的使用,参考Linux I/O编程

目录管理

相关系统调用或函数:

 系统调用 说明 
chmod 修改目录或者文件的模式(权限),是chmod命令的基础:
C
1
2
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
chown 超级用户可以修改文件的所有者:
C
1
2
3
4
#include <sys/types.h>
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
//支持数字格式的uid、gid,这些ID可以通过getuid、getgid获得
unlink
link
symlink

unlink 可以用来删除文件,该调用减少文件的链接数,成功返回0否则返回-1,要求用户具有文件所属目录的写和执行权限。如果链接数为0且没有进程打开之,则文件就会被删除
link 创建指向已有文件的新链接,新的目录项由path2参数给出
symlink 创建指向已有文件的符号链接,该系统调用不会增加目标文件的链接数

C
1
2
3
4
5
6
7
8
9
#include <unistd.h>
int unlink(const char *path);
int link(const char *path1, const char *path2);
int symlink(const char *path1, const char *path2);
 
//创建临时文件的技巧,创建后立即unlink
open(...);
unlink(...);
//该文件会在当前进程退出后自动清理删除
mkdir
rmdir
 分别用于创建和删除目录,rmdir只有在目录为空的时候才能删除
C
1
2
3
4
5
6
#include <sys/types.h>
#include <sys/stat.h>
int mkdir(const char *path, mode_t mode);
 
#include <unistd.h>
int rmdir(const char *path);
chdir 修改当前目录:
C
1
2
#include <unistd.h>
int chdir(const char *path);
函数 说明
getcwd 获取当前工作目录,如果缓冲区长度不够,返回NULL,否则返回缓冲区
C
1
2
#include <unistd.h>
char *getcwd(char *buf, size_t size);
目录扫描

虽然可以将目录作为普通文件一样打开读写,但是这种方式不具有可移植性。Linux下用于目录访问的标准库函数定义在dirent.h头中,这些函数使用结构体DIR作为目录操作的基础,DIR*被称为目录流。这些函数中常用的包括:

 函数 说明 
opendir

opendir打开一个目录并建立目录流,如果失败,返回NULL,在底层,目录流打开目录的文件描述符

C
1
2
3
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
readdir

返回一个包含了下一个目录项信息的dirent指针,每次调用返回下一个,遇到目录结尾则返回NULL。如果在迭代期间,其他进程创建/删除了文件,那么readdir不能保证所有文件被列出。

dirent指针包含两个数据项:

ino_t d_ino;/*文件Inode节点号*/char d_name[];/*文件的名字*/ 

C
1
2
3
#include <sys/types.h>
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
telldir

返回当前目录流迭代的位置,可以供后续seekdir调用进行重置

C
1
2
3
#include <sys/types.h>
#include <dirent.h>
long int telldir(DIR *dirp);
seekdir

设置目录流指针的位置

C
1
2
3
#include <sys/types.h>
#include <dirent.h>
void seekdir(DIR *dirp, long int loc);
closedir

 关闭目录流,并释放与之相关的资源

C
1
2
3
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
错误处理

很多文件I/O函数在失败后会设置外部变量errno的值,以提示失败的原因,程序必须在函数报告出错后立即检查errno,因为它可能被下一个函数调用覆盖。常见的错误代码包括:

错误代码 说明 
 EPERM 操作不允许
 ENOENT 文件或目录不存在
EINTR 系统调用被中断
EIO I/O错误
EBUSY 设备或资源忙
EEXIST 文件存在
EINVAL 无效参数
EMFILE 打开的文件过多
ENODEV 设备不存在
EISDIR 是一个目录
ENOTDIR 不是一个目录

下面两个函数用于错误代码的处理:

函数 说明 
strerror 将整型错误代码转换为字符串表示:
C
1
2
#include <string.h>
char *strerror(int errnum);
perror 将当前错误的字符串形式添加到缓冲区后面,在前缀后面添加冒号和空格:
C
1
2
#include <stdio.h>
void perror(const char *s);
其它主题
fcntl系统调用

该系统调用允许对底层的文件描述符进行更加细致的控制:

C
1
2
3
4
5
6
7
8
9
#include <fcntl.h>
int fcntl(int fildes, int cmd);
int fcntl(int fildes, int cmd, long arg);
 
//cmd表示需要执行的动作:
//F_DUPFD 复制并返回一个新的文件描述符
//F_GETFD 返回fcntl.h中定义的文件描述符标记
//F_SETFD 设置文件描述符标记
//F_GETFL 获取文件状态标记、访问模式
mmap函数

内存映射。该函数用于创建一段供多个程序共享的内存,其中一个程序对其的修改,另外一个程序会立即看到。

该函数创建了一个指向了一段内存区域的指针,该内存区域与文件描述符指向的文件的内容关联:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
//addr 起始内存地址,如果设置为0,则自动分配
//len 共享内存的长度
//prot设置共享内存的访问权限,以下位或:
//PROT_READ 该内存段可读
//PROT_WRITE 该内存段可写
//PROT_EXEC 该内存段可执行
//PROT_NONE 该内存段不能被访问
//flags控制程序对内存段的改变造成的影响:
//MAP_PRIVATE 内存段是私有的,修改是本地的,仅对当前进程有效
//MAP_SHARED 对内存段的修改被保留到磁盘
//MAP_FIXED 该段必须位于addr指定的地址
//fildes 共享内存关联的文件描述符
//off 共享内存访问文件内容的偏移值

使用msync函数可以把内存段中的部分或者全部写回到被映射的文件中,或者从文件中读出:

C
1
2
3
4
5
6
7
8
#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
//addr 起始地址
//len 长度
//flags 标记位:
//MS_ASYNC 执行异步写
//MS_SYNC 执行同步写
//MS_INVALIDATE 把数据读回到内存段

使用munmap函数可以释放内存段:

C
1
2
#include <sys/mman.h>
int munmap(void *addr, size_t len);
select系统调用

该系统调用在Linux下不仅仅可以用于网络I/O,普通文件I/O也被支持:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    char buffer[128];
    int result, nread;
    fd_set inputs, testfds;
    struct timeval timeout;
    FD_ZERO( &inputs ); //初始化为空白文件描述符集
    FD_SET( 0, &inputs ); //设置标准输入
    while ( 1 )
    {
        testfds = inputs;
        timeout.tv_sec = 2;
        timeout.tv_usec = 500000;
        //等待标准输入上具有数据可读,超时2.5秒
        result = select( FD_SETSIZE, &testfds, ( fd_set * ) NULL, ( fd_set * ) NULL, &timeout );
        switch ( result )
        {
            case 0 :
                printf( "超时\n" );
                break;
            case -1 :
                perror( "出错" );
                exit( 1 );
            default :
                if ( FD_ISSET( 0, &testfds ) )
                {
                    //标准输入已经就绪,可以读取
                }
                break;
        }
    }
}
← 备忘录模式
Linux编程知识集锦 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Linux进程间通信
  • Linux内核编程知识集锦
  • Linux信号、进程和会话
  • Linux网络编程
  • Linux编程知识集锦

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2