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]$ ./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;     //连到该文件的硬连接数目,刚建立的文件值为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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/375952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于objective-c的一点随笔

多日混迹cocoachina&#xff0c;这篇随笔算是积累点前人经验吧。 首先&#xff0c;要多逛两个很好的网站&#xff0c;Stack Overflow和github. 对于新人&#xff0c;一定要注意良好的格式和命名&#xff0c;不然对于日后回头看代码会非常难&#xff0c;oc不限制变量名称和函数名…

【转载】Ubuntu环境下配置Android Studio

之前学习Android开发的时候&#xff0c;一直跟各种教程一样&#xff0c;使用的是EclipseADT&#xff0c;主要是比较方便&#xff0c;容易上手&#xff0c;特别是对于习惯用Eclipse开发java的朋友来说&#xff0c;上手更是好无压力。但毕竟EclipseADT最多只能算Google的干儿子&a…

linux怎么进去vi编辑器,red hat enterprise linux开机怎么进入vi编辑器界面?

2015-05-21 回答后面输入直接打回车就会显示出来如下xx文件#■sa■jsadjk#■sa■jsklfjdl kl■sa&#xff1a;----现在是低行模式 现在按esc 进入第一个模式(命令模式)就变成下面xx文件#dsadjsadjk#dsadjsklfjdl kldsa现在我们按个v 就进入可是模式 现在我们一个移动光标选择要…

数据结构--链式线性表

环境&#xff1a;dev c #include<stdio.h> #include<stdlib.h>typedef struct LNode *List; typedef int ElementType;struct LNode{ElementType Data;List next; };int Length(List Ptrl); List FindKth(int k,List Ptrl); List Find(ElementType x,List Ptrl); …

linux査 到漠河 装apache,如何在Ubuntu上搭建一台安全的Apache Web服务器

满意答案1.安装Apache2使用下面这个命令&#xff0c;安装Apache2及其他库。1$ sudo apt-get -y install apt-get install apache2 apache2.2-common apache2-doc apache2-mpm-prefork apache2-utils libexpat1 ssl-cert libapache2-mod-php5 php5 php5-common php5-gd php5-cli…

vs2010 中添加 ActiveX Control Test Container工具

vs2010中的TSTCON( ActiveX Control Test Container )工具非自动安装&#xff0c;而是作为一个例程提供。所以应找到该例程&#xff0c;并编译&#xff1a; 如vs2010安装在默认路径则 1, 进入&#xff1a;C:\Program Files\Microsoft Visual Studio 10.0\Samples\1033&#xff…

linux c实现mypwd

这个其实很简单&#xff0c;只需要调用getcwd()这个函数就行了。 char *getcwd(char *buffer,int maxlen); 功能&#xff1a;获取当前工作目录 参数&#xff1a;buffer指向用来存储绝对路径的数组&#xff0c;maxlen绝对路径的字符大小 返回&#xff1a;成功则返回当前的工作目…

安装Discuz

1.下载Discuz 版本文件http://download.comsenz.com/DiscuzX/3.2/Discuz_X3.2_SC_GBK.zip2.下载PHPhttp://windows.php.net/downloads/releases/php-5.6.9-Win32-VC11-x64.zip把里面文件的php.ini-development 更名为php.ini Uncomment下列语句 extension_dir "ext"…

linux跑caffe模型的步骤,Caffe初步实践——使用训练好的模型完成语义分割任务

Caffe刚刚安装配置结束&#xff0c;乘热打铁&#xff01;(一)环境准备前面我有两篇文章写到caffe的搭建&#xff0c;第一篇cpu only &#xff0c;第二篇是在服务器上搭建的&#xff0c;其中第二篇因为硬件环境更佳我们的步骤稍显复杂。其实&#xff0c;第二篇也仅仅是caffe的初…

关于 Code First

第一感觉还是很新鲜的&#xff0c;你可以自由的控制数据结构。 比如&#xff0c;你想象oracle那样&#xff0c;给每个表增加4个字段&#xff0c;创建人&#xff0c;创建时间&#xff0c;更新人&#xff0c;更新时间。完全可以创建一个父类包含着四个属性&#xff08;甚至可以把…

灵悟礼品网上专卖店——新建数据库

一、小组成员&#xff1a; 洪雪意&#xff08;产品负责人&#xff09; 陈淑筠&#xff08;Master&#xff09; 二、组内人员任务情况 计划完成的任务的第三个模块&#xff1a;分析并建立数据库 已完成的任务&#xff1a; 任务的第三个模块&#xff1a; 陈淑筠&#xff08;负责…

操作系统上机题目(多进程1)

1、创建1个子进程2、程通过管道与子进程连接 子进程的标准输出连接到管道的写端主进程的标准输入连接到管道的读端3、进程中调用exec(“echo”, “echo”, “hello world”, NULL)4、进程中调用read(0, buf, sizeof(buf))&#xff0c;从标准输入中获取子进程发送的字符串&…

Oracle数据库dmp文件Dos命令下导入导出

Oracle数据库dmp文件Dos命令下导入导出 2013-03-09 18:22:52| 分类&#xff1a; Oracle |举报|字号 订阅 数据导出: 一. 导出工具exp 他是操作系统下一个可执行的文件,存放目录/Oracle_Home/bin. exp导出工具将数据库中数据备份压缩成一个二进制系统文件,可以在不同的OS间迁…

c语言报错找不到标识符,error C3861: “_T”: 找不到标识符

头天好好的程序&#xff0c;第二天一早就报错&#xff0c;还是莫名其妙的错误&#xff1a;atlconv.h等头文件中的“_T” 报错&#xff0c;百思不得其解&#xff0c;各种搜索而不得&#xff0c;整个人都崩溃了。出问题一定是有原因的&#xff0c;后来冷静下来&#xff0c;缕缕思…

js 面向对象插件写法,还是很好理解的

/** * Created by jiangtao on 2015/5/12. * name jihe */(function () { function gather(msg) { //适应参数 if (msg) { if (msg.imgFile ! undefined) { this.imgFile msg.imgFile; }; if (msg.wechatAppid ! undefined) { this.wechatAppid msg.wechatAppid; }; }; this.…

操作系统上机题目(多进程2)

1、主进程创建2个子进程&#xff0c;主进程通过两个管道分别与两个子进程连接2、第一个子进程计算从1加到50的和&#xff0c;并将结果通过管道送给父进程3、第一个子进程计算从50加到100的和&#xff0c;并将结果通过管道送给父进程4、父进程读取两个子进程的结果&#xff0c;将…

JavaScript 的简介

JavaScript 是一种基于对象和事件驱动的脚本语言。JavaScript和HTML一起实现网页与客户端的交互&#xff0c;从而可以开发客户端的应用程序。JavaScript是通过潜入在标准的HTML文件中实现的&#xff0c;可以直接控制浏览器窗口个元素以及页面内容。JavaScript一个重要的功能就是…

c语言大乐透编译,Excel大乐透摇号vba代码分享,说不定就中百万了呢

大家好我是Excel从零到一&#xff0c;今天闲来无聊做了一套大乐透摇号程序的vba代码分享给大家来看下效果Excel大乐透摇号vba代码分享&#xff0c;说不定就中百万了呢Sub 摇号()Dim i, a, test, s(1 To 35) As IntegerFor i 1 To 5line1: test Application.WorksheetFunction…

操作系统上机题目(多线程1)

主线程创建10个子线程 第0个子线程计算从01加到10的和 - 第1个子线程计算从11加到20的和 … 第9个子线程计算从91加到100的和 2. 主线程归并10个子线程的计算结果&#xff0c;最终结果为5050 本题必须使用线程参数来完成 #include<stdio.h> #include<unistd.h> #i…

纸上谈兵: 堆 (heap)

纸上谈兵: 堆 (heap) 作者&#xff1a;Vamei 出处&#xff1a;http://www.cnblogs.com/vamei 欢迎转载&#xff0c;也请保留这段声明。谢谢&#xff01; 堆(heap)又被为优先队列(priority queue)。尽管名为优先队列&#xff0c;但堆并不是队列。回忆一下&#xff0c;在队列中&a…