【Linux&Unix--open/close/write/read系统调用】

个人学习整理,如有不足之处,请不吝指教。转载请注明:@CSU-Max

系列博文:

Linux&Unix学习第一弹
-- 文件描述符与权限
 

Linux&Unix学习第二弹
-- exec 与 fock 系统调用

Linux&Unix学习第三弹
-- open/close/write/read系统调用

在 Unix/Linux 系统中,文件是一个很重要的概念,本文将介绍 Linux 中和文件相关的几个重要的系统调用--open-close-write-read 系统调用。

open系统调用

函数原型及解释

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//open -- 打开或创建文件
int open (
    const char *path,  /*pathname*/
    int flags,         /*flags*/
    mode_t perms       /*permissions (when creating)*/
) ;</span></span>

调用 open可以打开一个已经存在的文件(普通文件、特殊文件或命名管道),或创建一个新文件,但它只能创建普通文件(创建特殊文件需要使用 mknod,命名管道使用 mkfifo)。open返回是打开已存在的文件或创建新文件的文件描述符。文件一旦打开,read、
write、 lseek、 close以及其他调用就可以使用其返回的文件描述符。

打开已存在文件

首先我们来看一下 open函数的三个参数,path是已经存在的文件的路径;至于
flags参数,若值为 O_RDONLY ,就以只读方式打开文件,若值为 O_WDONLY,就以只写方式打开文件,若值为 O_RDWR,就以读写方式打开文件;而对于一个已经存在的文件,参数 perms是没有用的,通常将其省略,因此此种情况下 open调用只需两个参数。

open失败的原因很多,常见的有如下两种:

1.没有相应的文件访问权限

2.路径所指向的文件不存在

创建新文件

前面已经说到,当文件不存在时,open会创建一个新文件(仅能是普通文件),我们只需要用
or操作向 open的 flags参数中加入标志 O_CREAT即可。这样可以创建一个新的只读文件,但是这没有任何意义,因为所创建的新文件没有任何可读内容。因此一般需要 O_CREAT与 O_WRONLY或
O_RDWR一起使用,此时就需要 perms参数了。

例如:

ec_neg1(
fd = open(“/home/marc/newfile”,O_RDWR | O_CREAT, PERM_FILE) )

参数 perms仅在创建新文件时有效,对于一个已经存在的文件,它没有任何作用。

用户有时需要一个新的、没有任何数据的文件,即当文件已经存在,需要将其所有数据清除,并设置文件偏移量为0。标志
O_TRUNC可以实现此功能:

ec_neg1(
fd = open(“/home/marc/newfile”,O_WRONLY | O_CREAT | O_TRUNC, PERM_FILE) )

因为 O_TRUNC能够破坏数据,所以只要进程具有写权限,就可以清除已存在文件的数据,因为它是写形式的一种。但对于具有
O_RDONLY标志的文件,它就不起作用了。

O_WRONLY| O_CREAT | O_TRUNC这个组合是很常见的(创建或截短一个具有只写权限的文件),也有专门的相关的系统调用,即
creat系统调用。

creat系统调用

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//creat -- 创建或清空文件
int creat (
    const char *path, /*pathname*/
    mode_t perms      /*permissions*/
); </span></span>

采用 open打开一个已经存在的文件只需要第一个参数和第二个参数(path和
flags);使用 creat创建新文件仅需要第一个参数和第三个参数(path和 perms),实际上, creat仅仅是一个宏:

#define creat(path, perms) open(path,O_WRONLY | O_CREAT |O_TRUNC, perms)

当然我们也可以仅使用open 而放弃使用
creat,但是在早期,open只有两个参数,那时creat则无可替代。

关于open的flags参数

除了以上介绍的 open标志外,open还有许多标志,具体的如下表所示:


标志


解释


O_RDONLY


只读方式打开


O_WRONLY


只写方式打开


O_RDWR


读写方式打开


O_APPEND


每次写都追加到文件的尾端


O_CREAT


若文件不存在则创建文件


O_DSYNC


设置同步I/O方式


O_EXCL


如果文件已存在,则出错;必须与O_CREAT一起使用


O_NOCTTY


不将此设备作为控制终端


O_NONBLOCK


不等待命名管道或特殊文件准备好


O_RSYNC


设置同步I/O方式


O_SYNC


设置同步I/O方式


O_TRUNC


将其长度截短为0

close系统调用

函数原型及解释

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//close -- 关闭文件描述符
int close (
    int fd               /*file descriptor*/
);</span></span>

通过对 close进行分析,我们会发现close并没有做什么实质工作,它没有刷新任何内核缓冲区,而仅仅是使文件描述符可以重用。

如上图,当指向一个打开文件描述的所有文件描述都关闭时,将删除该打开文件描述。同样的,当指向一个信息节点的所有打开文件描述都被删除时,将删除该内存信息节点。

write系统调用

函数原型及解释

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//write -- 向文件描述符写
ssize_t write (
    int fd,              /*file descriptor*/
    const void *buf,     /*data to write*/
    size_t nbytes        /*amount to write*/
); </span></span>

write系统调用将 buf所指向的缓冲区的
n字节写入 fd 所描述ude打开文件中。

写操作从文件偏移量的当前位置开始执行,并且在完成之后,文件偏移量将增加所写入的字节数。若写入成功,返回值为已写入的字节数,出错则为
-1。

若设置了O_APPEND标志,写入前文件偏移量自动定位到文件的结尾。

本文仅讨论普通文件的写操作。

注:write也用与向管道,特殊文件和套接字写入数据,但是情况会有些不同,这些写操作可以阻塞(如它们可能正在等待可用数据)。如果阻塞了写操作,那么到达的信号会中断其操作。这种情况下写操作将返回-1,并将
errno设置为 EINTR。

write的真面目

看了前面的介绍,write似乎只是写入数据,接着返回结果。实际上并非如此。当用户调用
write系统调用时并不执行写操作,紧接着返回数据,它仅仅是将数据传递给内核的缓冲区。

当接收到 write请求时,先确保传入的文件描述符可以使用,接着将数据复制到内核中的缓冲区。以后,在某个方便的时候,系统会设法把这部分的数据写入磁盘中。若发现错误,就会设法在控制台输出错误,但是该进程不会返回这个错误(可能此时其已经终止运行了)。若在系统向磁盘写出这些数据之前,该进程或其他进程试图要读取这些数据,那么系统将从内核缓冲区为你读取这些数据。

总而言之,该进程不知道系统什么时候完成了请求,也不知道是否完成了请求。如果在该部分缓冲区的数据写出磁盘之前,有磁盘错误,或者由于某种原因内核停止了,那么你会发现要写的数据根本没有写到磁盘上,即使
write 没有报错。

由此我们可以看出,这是一种延迟写,那么延迟写有哪些我们需要关心的问题呢?

1.不能确定什么时候发生物理写操作

2.一个调用写操作的进程没有得到写错误的通知

3.物理写操作的顺序是无法操作的

writeall函数

writeall是一个非常方便实用的函数,当需要确保写入所有的内容时,可以使用此函数。

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//writeall
ssize_t writeall(int fd, const void *buf, size_t nbyte)
{
        ssize_t nwritten = 0;       //总共写出的字符数
        sszie_t n;                  //每次 write 操作写出的字符数 

        do{
                if((n = write(fd, &((const char *)buf)[nwriten], nbyte - nwritten)) == -1)
                {
                      if(errno == EINTR)        //阻塞时持续循环写出
                          continue;
                      else
                          return -1;
                }
                nwritten += n;
         }while(nwritten < nbyte);
         return nwritten;
}</span></span>

read系统调用

函数原型及解释

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//read -- 从文件描述符中读入
ssize_t read (
    int fd,              /*file descriptor*/
    void *buf,           /*address to receive data*/
    size_t nbytes        /*amount to read*/
); </span></span>

read系统调用与 write相反,它是从 fd所描述的打开文件中读取
buf所指缓冲区中的 n个字节。read从当前文件偏移量开始读数据,并且完成读操作后,文件偏移量将增加所读字节数。read的返回值是所读字节数、文件结束标志或者错误标志-1。读操作不受 O_APPEND标志的影响。

如果数据已经不在缓冲区中(由于以前的I/O操作),进程必须等待内核从磁盘得到数据。

与 write一样,从管道、特殊文件或套接字中读取数据时,read可以阻塞。此时读操作可能会被信号中断,结果返回值为-1,并把
errno 设置成 EINTR。

readall函数

类比 writeall函数,如果需要读所有的数据,则通过循环调用
read,readall函数就是这样的一个非常方便实用的函数。

<span style="font-family:Courier New;font-size:18px;"><span style="font-family:Courier New;font-size:18px;">//readall
ssize_t readall(int fd, const void *buf, size_t nbyte)
{
        ssize_t nread == 0;        //总共读取的字符数
        ssize_t n;                 //每次 read 操作读取的字符数 

        do{
                if((n = read(fd, &((const char *)buf)[nread], nbyte - nread)) == -1)
                {
                      if(errno == EINTR)        //阻塞时持续循环读取
                          continue;
                      else
                          return -1;
                }
                nread += n;
         }while(nread < nbyte);
         return nread;
}</span></span>

****************************************************************

*   转载请注明出处:  @CSU-Max   http://blog.csdn.net/csu_max 
  *

****************************************************************

【Linux&Unix--open/close/write/read系统调用】,布布扣,bubuko.com

时间: 2024-08-11 09:57:51

【Linux&Unix--open/close/write/read系统调用】的相关文章

Linux/Unix C编程之的perror函数,strerror函数,errno

#include <stdio.h> // void perror(const char *msg); #include <string.h> // char *strerror(int errnum); #include <errno.h> //errno ? errno 是错误代码,在 errno.h头文件中: perror是错误输出函数,输出格式为:msg:errno对应的错误信息(加上一个换行符): strerror?是通过参数 errnum (就是errno)

linux/unix man用法

最近开始学习linux c编程,需要用到很多库函数和c标准库函数,发现原来man命令还可以用来查询函数原型. 比如输入man lseek可以获取函数lseek的原型和所属头文件. 有些函数名既是linux命令又是系统调用,如mkdir,可以输入man 2 mkdir获取该函数原型.只输入man mkdir获取的是命令mkdir的帮助. 对于库函数可以输入man 3 <库函数名>  获得帮助信息.man 3 opendir 对于系统开发经常需要查询函数还是非常方便的,另外在vim中可以用shif

《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

<Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h>文件中的常量. 通过cat 命令查看: [email protected]:~/Code/tlpi$ cat /usr/include/limits.h /* Copyright (C) 1991, 1992, 1996, 1997, 1998, 1999, 2000, 2005 Free Software

《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲)

<Linux/Unix系统编程手册>读书笔记 目录 第13章 这章主要将了关于文件I/O的缓冲. 系统I/O调用(即内核)和C语言标准库I/O函数(即stdio函数)在对磁盘进行操作的时候都会发生缓冲.通过缓冲可以在一定程度上将用户空间与实际的物理设备分离,还可以减少内核访问磁盘的次数. 先来看看关于内核缓冲区高速缓冲:read和write调用在对磁盘文件进行操作的时候不会直接访问磁盘,如下图所示. 例如:write(fd, "abc", 3) write调用会将"

编写Linux/Unix守护进程

原文: http://www.cnblogs.com/haimingwey/archive/2012/04/25/2470190.html 守护进程在Linux/Unix系统中有着广泛的应用.有时,开发人员也想把自己的程序变成守护进程.在创建一个守护进程的时候,要接触到子进程.进程组.会晤期.信号机制.文件.目录和控制终端等多个概念.因此守护进程还是比较复杂的,在这里详细地讨论Linux/Unix的守护进程的编写,总结出八条经验,并给出应用范例. 编程要点 1.屏蔽一些有关控制终端操作的信号.防

《Linux/Unix系统编程手册》读书笔记9(文件属性)

<Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有更深入的研究一定会写出来. 下图为磁盘分区与文件系统的关系 文件系统中的文件(目录)在i-node表上都有唯一的记录(i-node).i-node通过数据块指针指向数据块,这些数据块就是该i-node对应的文件的数据. i-node与数据块的关系如下: 因为Linux支持很多类型的文件系统,但是每种文件系统的

Linux/Unix系统编程手册--SOCKET章节读书笔记

SOCKET章节读书笔记 强烈推荐Linux/Unix系统编程手册,号称超越APUE的神书. backlog含义 #include <sys/socket.h> int listen(int socketfd, int backlog) backlog参数限制未决连接(未accept)的数量,在这个数量之内,connect会立刻成功. Linux上上限为128,定义在 udp已连接socket udp socket也是可以调用connect()的,这种叫已连接socket,内核会记录这个soc

【Linux&amp;amp;Unix--open/close/write/read系统调用】

个人学习整理.如有不足之处,请不吝不吝赐教. 转载请注明:@CSU-Max 系列博文: Linux&Unix学习第一弹 -- 文件描写叙述符与权限  Linux&Unix学习第二弹 -- exec 与 fock 系统调用 Linux&Unix学习第三弹 -- open/close/write/read系统调用 在 Unix/Linux 系统中,文件是一个非常重要的概念,本文将介绍 Linux 中和文件相关的几个重要的系统调用--open-close-write-read 系统调用.

Linux/UNIX系统编程手册 PDF下载

网盘下载地址:Linux/UNIX系统编程手册 PDF下载 – 易分享电子书PDF资源网 作者: Michael Kerrisk 出版社: 人民邮电出版社 原作名: The Linux Programming Interface: A Linux and UNIX System Programming Handbook 译者: 孙剑 许从年 董健 / 孙余强 郭光伟 陈舸 出版年: 2014-1 页数: 1176 定价: 158 装帧: 平装 内容简介 · · · · · · <linux/un

六、Linux/UNIX操作命令积累【kill、netstat、df、du】

在使用Linux/UNIX下,经常会使用文本界面去设置系统或操作系统,作者本人在工作的过程也在不断接触这方面的命令,所以为此特酝酿.准备.开始了本文的编写.本文主要记录自己平时遇到的一些Linux/UNIX下操作命令,记录与整理一下,一可加深印象,二可记录分享.希望各位看官,对于不合适的或有歧义的地方,给予指明与说明,以便共同学习与提高. [转载使用,请注明出处:http://blog.csdn.net/mahoking] 023 kill命令:查看进程及杀死进程 查看进程使用ps命令,本例演示