我是靠谱客的博主 眯眯眼灰狼,最近开发中收集的这篇文章主要介绍linux系统调用open、write、close、read以及stat函数详解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

学习笔记 参考链接1 、参考链接2以及百度百科
在进行C语言学习的时候我们了解到了C语言相关的一些IO操作,如fopen,fwrite,fread,fprintf,fclose等相关函数,他们都是由C库函数提供的一些函数,是将操作系统的系统调用加以封装,虽说Linux是由C语言实现的,但为了使我们更加的了解Linux,就需要了解更接近与底层的一些IO操作,因此就需要来了解下基本的系统调用—open,write,read,close
首先我们来了解下open,write,read,close的系统调用

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);

open有三个参数
pathname:要打开或创建的目标文件名
flags:对文件进行多种操作也就有有多个参数,这多个参数可以进行或运算,即就是flags
参数:
O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读,写打开
O_CREAT:若文件不存在,创建文件
O_APPEND:追加写
参数1,2,3,必须制定一个且只能制定一个,使用参数4,必须使用open的第三个参数mode:新文件的访问权限
返回值:成功:新打开文件的文件描述符(fd)
失败:-1

write

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

fd:文件描述符
buf:写入的缓冲区
count:写的字符长度,也就是看你需要写多少
返回值:
如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。
read

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

fd:文件描述符
buf:读入的缓冲区
count:写的字符长度,也就是看你需要写多少
返回值
如果顺利read()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。
close

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

close的参数就相对简单了,这一个操作是不能遗漏的,只要了使用fd就必须close它
在这几个函数中都涉及到了关键的参数fd,因此要了解这几个函数,就必须先了解下文件描述符(fd)。
什么是文件描述符,这是一个相对抽象的概念,我们先来看看下面这张图
这里写图片描述
在PCB结构体中存在一个files指针,它指向一个file_struct结构体,而在file_struct结构体中存在一个file* fd数组,这个数组里面存放的是file指针,用来指向不同的file文件,而fd就可以理解为这个指针数组的下标,因此要打开一个文件,我们就可以拿到该文件的fd就可以了。
fd的分配原则:
files_struct数组当中,使用没有被使用的最小下标,作为新的文件描述符。
操作系统默认使用了该数组的前三个元素,0号下标指向标准输入(stdin),1号下标指向标准输出(stdout),2号下标指向标准错误(stderr)。
因此正常情况下,新的fd都是从3开始的,但如果我们关闭了默认的fd,新的文件的fd就从关闭的fd处开始。
说到了fd,我们就不得不来区分下FILEfd
FILE是C库当中提供的一个结构体,而fd是系统调用,更加接近于底层,因此FILE中必定封装了fd。
我们可以来看看FILE的结构体:
typedef struct _IO_FILE FILE;在/usr/include/stdio.h
它的结构体中有这么一段

struct _IO_FILE {
int _flags;
/* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note:
Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr;
/* Current read pointer */
char* _IO_read_end;
/* End of get area. */
char* _IO_read_base;
/* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr;
/* Current put pointer. */
char* _IO_write_end;
/* End of put area. */
char* _IO_buf_base;
/* Start of reserve area. */
char* _IO_buf_end;
/* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base;
/* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;//fd的封装

可以看到int_fileno就是对fd的封装,在这一部分的开头有一大段跟缓冲区相关的内容,为什么要诺列出它呢,首先可以来看一个很诡异的例子:

#include <stdio.h>

#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(){
const char *msg1 = "hello printfn";
const char *msg2 = "hello fwriten";
const char *msg3 = "hello writen";
printf(msg1);
fwrite(msg2, 1, strlen(msg2), stdout);
write(1, msg3, strlen(msg3));
fork();
return 0;
}

运行结果:

[rlh@localhost test]$ ./hello
hello printf
hello fwrite
hello write

但当我们对进程实现输出重定向,你就会发现诡异的事情:

[rlh@localhost test]$ ./hello > file
[rlh@localhost test]$ cat file
hello write
hello printf
hello fwrite
hello printf
hello fwrite

这是为什么呢,这是跟C库的缓冲数据有关,C库缓冲数据分为三种(1)、无缓冲(2)、行缓冲(3)、全缓冲。

行缓冲就是往显示器上写,全缓冲就是往文件里写。

在上面的现象中,write不受影响是因为它属于系统调用,没有缓冲区,而printf和fwrite会自带缓冲区,当发生重定向到普通文件的时候,它就会从行缓冲转变为全缓冲,也就是会往文件里面写写,但是我们缓冲区里的数据,即使fork也不会立即被刷新,当进程退出后会统一刷新,写入文件,但是fork的时候会发生写时拷贝,也就是当父进程准备刷新的时候,子进程就已经有了一份相同的数据,所以就会产生上面的现象。

了解下重定向。
重定向分为三种:
输出重定向(>) 也就是关闭fd为1下标所指向的内容
输入重定向(<) 同理就是关闭fd为0下标所指向的内容
追加重定向(>>) 后面多一个追加选项

stat函数

#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf);

函数说明: 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
返回值: 执行成功则返回0,失败返回-1,错误代码存于errno

错误代码:
ENOENT 参数file_name指定的文件不存在
ENOTDIR 路径中的目录存在但却非真正的目录
ELOOP 欲打开的文件有过多符号连接问题,上限为16符号连接
EFAULT 参数buf为无效指针,指向无法存在的内存空间
EACCESS 存取文件时被拒绝
ENOMEM 核心内存不足
ENAMETOOLONG 参数file_name的路径名称太长
eg:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main() {
struct stat buf;
stat("/etc/hosts", &buf);
printf("/etc/hosts file size = %d/n", buf.st_size);
}
struct stat {
dev_t
st_dev;
//文件的设备编号
ino_t
st_ino;
//节点
mode_t
st_mode;
//文件的类型和存取的权限
nlink_t
st_nlink;
//连到该文件的硬连接数目,刚建立的文件值为1
uid_t
st_uid;
//用户ID
gid_t
st_gid;
//组ID
dev_t
st_rdev;
//(设备类型)若此文件为设备文件,则为其设备编号
off_t
st_size;
//文件字节数(文件大小)
unsigned long st_blksize;
//块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks;
//块数
time_t
st_atime;
//最后一次访问时间
time_t
st_mtime;
//最后一次修改时间
time_t
st_ctime;
//最后一次改变时间(指属性)
};

先前所描述的st_mode 则定义了下列数种情况:

 S_IFMT
0170000
文件类型的位遮罩
S_IFSOCK 0140000
scoket
S_IFLNK 0120000
符号连接
S_IFREG 0100000
一般文件
S_IFBLK 0060000
区块装置
S_IFDIR 0040000
目录
S_IFCHR 0020000
字符装置
S_IFIFO 0010000
先进先出
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
其他用户具可执行权限

上述的文件类型在POSIX中定义了检查这些类型的宏定义:


S_ISLNK (st_mode)
判断是否为符号连接
S_ISREG (st_mode)
是否为一般文件
S_ISDIR (st_mode)
是否为目录
S_ISCHR (st_mode)
是否为字符装置文件
S_ISBLK (s3e)
是否为先进先出
S_ISSOCK (st_mode)
是否为socket

最后

以上就是眯眯眼灰狼为你收集整理的linux系统调用open、write、close、read以及stat函数详解的全部内容,希望文章能够帮你解决linux系统调用open、write、close、read以及stat函数详解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部