700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > linux系统调用open write close read以及stat函数详解

linux系统调用open write close read以及stat函数详解

时间:2024-05-21 23:34:36

相关推荐

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 printf\n";const char *msg2 = "hello fwrite\n";const char *msg3 = "hello write\n";printf(msg1);fwrite(msg2, 1, strlen(msg2), stdout);write(1, msg3, strlen(msg3));fork();return 0;}

运行结果:

[rlh@localhost test]$ ./hellohello 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;//连到该文件的硬连接数目,刚建立的文件值为1uid_t st_uid; //用户IDgid_t st_gid; //组IDdev_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 scoketS_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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。