您的位置:首页 > 其它

关于系统级I/O函数

2020-02-01 04:53 781 查看

Unix I/O模型是在操作系统内核中实现的。应用程序可以通过 open()、read()、write()、lseek()、stat()、close() 函数来访问Unix I/O。

在Unix I/O函数上层,C应用程序对其进行了封装,产生了标准I/O函数和RIO函数。

1.open()函数

进程通过调用open函数来打开一个已存在的文件或者创建一个新文件

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

int open(char *filename, int flags, mode_t mode);
/*返回:若成功则为新的文件描述符,若出错则为-1*/

参数说明:
open函数将filename转换为一个文件描述符,并返回描述符数字,数值为当前进程中未打开的最小描述符;
flags参数指明进程打算如何访问这个文件,可以是一个或者更多位掩码的或;
mode参数制定了新文件的访问权限位,只在创建新文件时有用。
创建新文件:
open(pathname, O_WRONLY|O_CREAT|O_TRUNC, mode)

2.read()函数

应用程序通过调用read函数执行输入功能

#include<unistd.h>

ssize_t read(int fd, void *buf, size_t n);
/*返回:若成功则为读的字节数,若EOF则为0,若出错为-1*/

read函数从文件描述符为fd的当前文件位置赋值最多n个字节到内存位置buf。

ssize_t和size_t:
size_t被定义为unsigned long,而ssize_t被定义为long,是无符号数与有符号数的区别。read函数返回一个有符号的大小而不是一个无符号大小,这是因为出错时它必须返回-1。若返回值为无符号数,-1就会变成一个无符号的最大值,与我们要求的不相符

3.write()函数

应用程序通过调用write函数执行输出功能

#include<unistd.h>

ssize_t write(int fd, void *buf, size_t n);
/*返回:若成功则为写的字节数,若出错则为-1*/

write函数从内存位置buf复制至多n个字节到描述符fd的当前文件位置。

在某些情况下,read和write传送的字节比应用程序要求的要少。但这些不足值不表示有错误,出现这样情况的原因有:

- 读时遇到EOF
- 从终端读文本行
- 读和写网络套接字

4.lseek()函数

应用程序通过调用lseek函数来显示地修改当前文件的位置

#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/*返回:若成功则为新的偏移量,若出错则为-1*/

参数说明:
offset为偏移量,该值可正可负;
whence为定位的模式:

  • SEEK_SET:基于文件开头定位,新光标位置=文件开头+offset(此时offset>0)
  • SEEK_CUR:基于当前光标位置定位,新光标位置=当前光标位置+offset(可正可负)
  • SEEK_END:基于文件末尾定位,新光标位置=文件末尾+offset(可正可负)

5.stat()函数

应用程序通过调用stat函数来检索到关于文件的信息(也称文件的元数据)

#include<unistd.h>
#include<sys/stat.h>

int stat(const *filename, struct stat *buf);
/*返回:若成功则为0,若出错则为-1*/

stat函数以一个文件名作为输入,并填写以下的一个stat数据结构中的各个成员:

struct stat{
dev_t         st_dev;       /* Device */
ino_t         st_ino;       /* inode */
mode_t        st_mode;      /* Protection and file type */
nlink_t       st_nlink;     /* Number of hard links */
uid_t         st_uid;       /* User ID of owner */
gid_t         st_gid;       /* Group ID of owner */
dev_t         st_rdev;      /* Device type(if inode device) */
off_t         st_size;      /* Total size,in bytes */
unsigned long st_blksize;   /* Block size for filesystem I/O */
unsigned long st_blocks;    /* Number of blocks allocated */
time_t        st_atime;     /* Time of last access */
time_t        st_mtime;     /* Time of last modification */
time_t        st_ctime;     /* Time of last change */

6.close()函数

进程通过调用close函数来关闭一个已经打开的文件

#include<unistd.h>

int close(int fd);
/*返回:若成功则为0,若出错则为-1*/

以上就是Unix中的6个基础函数。
在实际使用时,一定要考虑出错时的输出情况,因此可自己将基础函数进行封装,在封装后的函数内部处理出错的情况,在使用时就直接调用封装后的函数。但注意,由于新的函数不是系统自带的处理函数,在调用前一定要对其进行链接,否则会报错。

以open函数为例,在调用时,可直接选择Open函数来打开或创建文件:

int Open(const char *pathname, int flags, mode_t mode)
{
int rc;
if((rc = open(pathname, flags, mode)) < 0)
unix_error("Open error");
return rc;
}

还有一个比较重要的函数,即dup2函数,以实现I/O重定向功能。

#include<unistd.h>

int dup2(int oldfd, int new fd);
/*返回:若成功则为非负的描述符,若出错则为-1*/

dup2函数赋值描述符表表项oldfd到描述符表项newfd,覆盖描述符表表项newfd以前的内容。如果newfd已经打开了,dup2会在复制oldfd之前关闭newfd。

有关用法举例(foobar.txt文件由6个ASCII码字符“foobar”组成):

#include"csapp.h"
/*csapp.h为自定义的头文件,里面封装了Unix中的I/O函数*/

int main()
{
int fd1, fd2;
char c;

fd1 = Open("foobar.txt", O_RDONLY, 0);
fd2 = Open("foobar.txt", O_RDINLY, 0);
Read(fd2, &c, 1);
Dup2(fd2, fd1);
Read(fd1, &c, 1);
printf("c=%c\n", c);
exit(0);
}

在上述程序中,首先将foobar.txt文件同时打开两份。
由于系统自带的标准输入、标准输出、标准错误文件占用了0、1、2三个描述符,因此最新打开的文件描述符从3开始。则fd1=3,fd2=4。
在fd2中读取1个字符,即“f”,此时fd2的光标位置移动到了第二个字符处。
调用封装后的dup2函数,把fd2的内容覆盖到fd1上,此时对fd1操作也同时操作了fd2。因此在fd1中的光标位置也位于第二个字符处。
再从fd1中读取一个字符,即“o”。
因此该程序的输出结果为 c=o

  • 点赞
  • 收藏
  • 分享
  • 文章举报
沉浮sy 发布了4 篇原创文章 · 获赞 4 · 访问量 115 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: