我是靠谱客的博主 眯眯眼灰狼,这篇文章主要介绍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

复制代码
1
2
3
4
#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

复制代码
1
2
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);

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

复制代码
1
2
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);

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

复制代码
1
2
#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
它的结构体中有这么一段

复制代码
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
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的封装,在这一部分的开头有一大段跟缓冲区相关的内容,为什么要诺列出它呢,首先可以来看一个很诡异的例子:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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; }

运行结果:

复制代码
1
2
3
4
[rlh@localhost test]$ ./hello hello printf hello fwrite hello write

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

复制代码
1
2
3
4
5
6
7
[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函数

复制代码
1
2
3
#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:

复制代码
1
2
3
4
5
6
7
8
#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); }
复制代码
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
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 则定义了下列数种情况:

复制代码
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
40
41
42
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中定义了检查这些类型的宏定义:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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函数详解内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部