unix环境高级编程(上)-文件篇

目录

前言

unix基础知识

unix标准化和实现

unix提供的文件IO

文件和目录

标准IO

系统数据文件

前言

笔者将《unix环境高级编程》主要内容总结为三篇:文件篇进程篇高级io和进程间通讯三大板块。本文是unix环境高级编程系列文章第一篇:文件篇。该篇主要包括:node

unix基础知识

介绍了unix的体系结构,以及unix中的文件和目录,输入输出,程序和进程,信号等基本概念shell

unix标准与实现

标准包括C语言的标准和操做系统标准,实现包括BSD,FreeBSD,Linux,Solari,Mac os等数据库

unix内核提供的文件io函数

包括文件描述符,对文件的打开,关闭,定位,读,写,改变文件属性操做。内核IO调用基于文件描述符。还介绍了文件的底层数据结构,了解数据结构以后就能理解文件是如何支持共享的编程

文件和目录

主要介绍文件的属性和属性对应的数据结构,以及各个字段控制的问访问权限,文件类型等。unix中一切皆文件,这些文件包括:普通文件,目录文件,块特殊文件,字符特殊文件,FIFO,套接字和符号连接。最后结束UFS文件系统。数组

标准IO函数

标准io解决了内核io的不少细节问题,包括缓冲区分配。全部操做基于流和File对象缓存

系统数据文件

最后介绍系统提供的一些数据文件,包括口令文件,阴影文件,朱文杰,登陆帐号文件,服务数据文件,协议数据文件,网络数据文件等bash

一. unix基础知识

1. unix体系结构

  • 操做系统是一种特殊的软件,它控制计算机硬件资源,提供程序运行环境
  • 此软件称为内核,它相对较小,位于环境的中心
  • 内核的接口被称为系统调用
  • 公共函数库构建在系统调用接口上
  • 系统调用通常比普通函数调用须要花费更多时间
  • 应用软件能够调用公共函数库或者使用系统调用

2. 文件和目录

  • 文件系统是目录和文件组成的一种层次结构
  • 目录的起点称为根,名称为/符号
  • 目录是包含不少目录项的文件
  • 逻辑上可认为每一个目录项都包含文件名和文件属性。物理上是不包含的,由于一个文件可被屡次硬连接
  • 文件属性包括:类型(普通文件,目录),大小,全部者,权限,修改时间等。state和fstate函数返回文件属性。

3. 输入和输出

3.1 文件描述符

一般是一个小的非负整数,内核用它标识一个特定进程正在访问的文件服务器

3.2 标准输入,标准输出,标准出错

每当运行一个新程序时,shell都为其打开三个文件描述符:网络

说明 文件描述符 头文件
标准输入 0 <unistd.h> STDIN_FILENO
标准输出 1 <unistd.h> STDOUT_FILENO
标准出错 2 <unistd.h> STDERR_FILENO

3.3 不用缓冲的io

  • 函数open,read,write,lseek,close提供了不用缓冲的io
  • 这些函数都使用文件描述符
  • 头文件为<fcntl.h>

3.4 标准io

  • 标准io提供一种带缓冲io的接口
  • 使用标准io无需担忧如何选取最佳缓冲区大小,且简化了堆输入行的处理
  • 标准io头文件为<stdio.h>

4. 程序和进程

4.1 程序

  • 存放在磁盘,处于某个目录中的可执行文件
  • exec函数执行时,内核将程序读入存储器并执行

4.2 进程

  • 程序的执行实例被称为进程
  • 每一个进程都有一个惟一的数字标识,称为进程ID

4.3 进程控制

进程控制的主要函数:fork,exec和waitpid数据结构

4.4 线程

  • 进程内全部线程共享同一个地址空间,文件描述符,栈,进程相关属性
  • 线程访问共享数据时须要采起同步措施避免不一致性
  • 线程也用ID标识,可是只在它所属进程内起做用

5. 信号

  • 信号是通知进程已发生某种状况的一种技术
  • 进程如何处理信号有三种选择:
    • 忽略该信号
    • 按系统默认方式处理
    • 捕捉该信号:提供一个函数,信号发生时调用该函数。调用signal函数,第一个参数为信号名称,第二个参数为处理函数

6. 时间值

unix系统一直使用两种不一样的时间值

  • 日历时间:UTC时间,用time_t表示。记录自1970年1月1日以来锁通过的秒数
  • 进程时间:cpu时间,用clock_t表示。已时钟滴答计算

二. unix标准化和实现

1. unix标准化

  • ISO c:c语言国际化标准
  • POSIX:可移植的操做系统接口(protable operating system interface)

2. unix实现

  • SVR4:AT&T的UNIX系统实验室产品,第一版了系统V接口定义
  • BSD:加州伯克利分校研究和开发的,含有AT&T许可证的代码
  • FreeBSD:BSD去除AT&T许可证代码后,彻底免费的版本
  • Linux:1991年Linux开发的一款被目前普遍使用的unix操做系统
  • Mac OS:核心系统是Darwin,基于Mach内核和FreeBSD的组合
  • Solaris:sun公司开发的unix系统版本

三. unix提供的文件IO

1. 文件描述符

  • 内核中,全部打开的文件都经过文件描述符引用
  • 打开,新建时,内核向进程返回一个文件描述符
  • 读写文件时,将文件描述符传给read和write

2. open

  • 做用:建立或打开一个文件
  • pathname参数:文件名字
  • flag参数:由如下值进行“或”组成
    • O_RDONLY:只读
    • O_WRONLY:只写
    • O_RDWR:读写
    • O_APPEND:追加到末尾
    • O_CREATE:文件不存在就建立
    • O_EXCL:同时指定O_CREATE时,若是文件存在,就会出错。使测试和建立成为原子操做
    • O_TRUNC:将文件长度截短为0
    • O_NOCTTY:控制终端相关
    • O_NONBLOCK:非阻塞模式
  • mode参数:文件访问权限,仅新建文件时使用该参数

3. create

  • 做用:建立文件
  • 等价于
    open(pathname, O_WRONLY|O_CREATE|O_TRUNC, mode)
    复制代码

4. close

  • 做用:关闭文件
  • 关闭会释放加在该文件上的全部记录锁
  • 进程终止时,内核自动关闭它打开的文件,故能够不用显示调用close

5. lseek

  • 做用:设置打开文件的偏移量
  • 默认偏移量为0,若是设置O_APPEND属性,默认偏移量为文件末尾
  • whence的取值:
    • SEEK_SET:设置文件偏移为pos值
    • SEEK_CUR:设置文件偏移为当前位置+pos
    • SEEK_END:设置文件偏移为文件长度+pos

6. read

  • 做用:从打开的文件中读数据
  • 读取成功,返回读到的字节数。读到末尾,返回0。
  • 致使读到的字节数小于要求读字节数的状况:
    • 普通文件:读到达到要求字节数时,已经读到文件结尾了
    • 终端设备文件:一次最多读一行
    • 网络数据:缓存区大小小于要读字节
    • 管道文件:管道包含的字节小于要读字节

7. write

  • 做用:向打开文件中写数据
  • 返回值一般与nbyte相同,不然出错
  • 写成功后,文件偏移量增长写入字节数量

9. 文件共享

9.1 打开文件的内核数据结构

unix支持在不一样进程间共享打开的文件,unix内核使用什么数据结构来支持这种共享呢?

  • 进程表记录来全部的进程
  • 每一个进程都有一个记录项,用来记录打开文件的文件描述表
  • 文件描述符的每一项包括:
    • 文件描述符标识
    • 指向文件表项的指针
  • 文件表项由内核维护,每一项包括:
    • 文件状态标识(读,写,同步,阻塞等)
    • 当前文件偏移量
    • 指向该文件v节点表项的指针
  • 每一个打开文件都有v节点(v-node)结构,这些信息是打开文件时从磁盘读入内存的。包括:
    • 文件类型
    • 对此文件进行各类操做的指针
    • i节点信息(索引信息):包括长度,全部者,所在设备,磁盘位置指针等

9.2 两个独立进程各自打开同一文件

  • 给定的文件,只有一个v节点表项
  • 每一个进程都有本身的文件表项,以使本身有独立的文件偏移量

9.3 两个独立进程共享同一个文件表项

  • 使用dup和fork函数时,父子进程对于每个文件描述符,都共享同一个文件表项,达到文件共享的目的

9.4 建立共享文件的函数

  • dup:返回的文件描述符为可用的最小值
  • dup2:返回fieldes2指定的描述符。若是fieldes2已经打开,就关闭。若是fieldes=fieldes2,不关闭,直接返回。
  • fcntl:也能够建立共享文件

10. 原子操做

  • 原子操做:指多步组成的操做,
  • 任何一个须要调用多个函数的操做都不多是原子操做,由于中间可能会挂起该进程
  • unix提供了一些函数,使多个操做成为一个“原子操做”
  • O_APPEND标识:lseek和write的原子操做
  • pread:lseek和read的原子操做
  • pwrite:lseek和write的原子操做
  • 调用open时,经过制定O_CREAT和O_EXCL参数,将建立文件做为原子操做

11. sync, fsync, fdatasync函数

这几个函数出现的背景:unix提供的延时写功能,经过提供缓冲区以减小磁盘读写次数,可是下降了文件内容更新速度,这几个函数用于保证缓冲区内容与文件内容的同步,保证一致性。

  • sync:将修改的快缓冲区排入写队列,立马返回,不等待真正写磁盘
  • fsync:针对指定的文件描述符起做用,且等待磁盘写完才返回。同步内容包括数据和文件属性。适用于数据库系统。
  • fdatasync:包括fsync的功能,可是只同步数据,不一样步文件属性。

12. fcntl函数

  • 做用:改变已打开文件的性质
  • 参数cmd的取值和做用:
    • F_DUPFD:复制一个现有的文件描述符
    • F_GETFD: 设置文件描述符标记
    • F_SETFD: 得到文件描述符标记
    • F_GETFL: 设置文件状态标记:读,写,追加,阻塞等。
    • F_SETFL: 得到文件状态标记
    • F_GETOWN: 设置异步io全部权
    • F_SEGOWN: 得到异步io全部权
    • F_GETLK:得到记录锁
    • F_SETLK:设置记录锁
    • F_SETLKW:设置记录锁

四. 文件和目录

1. 文件属性

1.1 表示文件属性的数据结构:struct stat

```
struct stat {
    mode_t     st_mode;       //文件模式,包含文件类型,用户id,组id,访问权限(9种)等信息
    ino_t      st_ino;       //inode节点号
    dev_t      st_dev;        //设备号码
    dev_t      st_rdev;       //特殊设备号码
    nlink_t    st_nlink;      //文件的链接数
    uid_t      st_uid;        //文件全部者
    gid_t      st_gid;        //文件全部者对应的组
    off_t      st_size;       //普通文件,对应的文件字节数
    time_t     st_atime;      //文件数据最后被访问的时间
    time_t     st_mtime;      //文件数据最后被修改的时间
    time_t     st_ctime;      //文件状态(i节点状态)的最后修改时间
    blksize_t st_blksize;    //文件内容对应的块大小
    blkcnt_t   st_blocks;     //伟建内容对应的块数量
  };
```
复制代码

1.2 如何获取文件属性

  • state:根据文件名获取属性
  • fstate:根据描述符获取属性
  • lstate:返回符号连接的属性

1.3 修改属性的部分方法

  • 访问时间和修改时间: utime函数,参数为struct utimbuf,每一项都是utc时间
  • 文件用户id和组id:chown,fchown,lchown

2. 文件类型:

2.1 st_mode字段控制的文件类型

  • S_ISREG:普通文件。文本或二进制;可执行文件有固定的可被内核识别的格式。
  • S_ISDIR:目录文件。包含其余文件的名字以及指向与这些文件有关信息的指针。
  • S_ISBLK:块特殊文件。提供堆设备(如磁盘)带缓冲的访问,访问长度固定。
  • S_ISCHR:字符特殊文件。提供堆设备(如磁盘)不带缓冲的访问,访问长度不固定。
  • S_ISFIFO:FIFO,命名管道。用于进程间通讯
  • S_ISSOCK:套接字。用于网络间进程通讯
  • S_ISLINK:符号连接。指向另外一个文件

2.2 stat结构体自己控制的文件类型

  • S_TYPEISMQ:消息队列
  • S_TYPEISSEM:信号量
  • S_TYPEISSHM:共享存储对象

3. 文件访问权限

  • 权限位保存在st_mode属性中
  • 9个访问权限位对应的值为:
  • 更改文件访问权限的函数:chmod和fchmod

4. UFS文件系统

4.1 磁盘,分区和文件系统图

  • 一个磁盘分为多个分区,每一个分区能够包含一个文件系统
  • i节点是固定长度的记录项

4.2 详细的柱面组的i节点和数据块

  • 每一个柱面包括:i节点数组,数据库,目录块
  • 每一个i节点包含文件的大部分信息:文件类型,访问权限,长度,占用的实际数据库。(stat结构大多数信息取自i节点)
  • 每一个目录块包括:目录名称,i节点号
  • 同一个i节点,能够被不一样的目录指向,i节点的连接计数统计指向的数量
  • 文件更名时,实际内容并未移动,只是构造一个新目录项,指向现有的节点,并解除旧记录项的连接

5. 硬连接

硬连接直接指向文件的i节点

5.1 建立一个指向现有文件的连接:link方法

  • 若是newpath已经存在,返回出错
  • 只能建立newpath中最后一个份量,路径中其余部分必须已经存在
  • 不少文件系统不容许堆目录建立硬连接
  • 超级用户能直接建立目录硬连接

5.2 删除一个现有的连接项:unlink方法

  • 将path所引用的文件的连接数减1
  • 只有当链接技术为0,该文件的内容才被删除
  • 对于文件,可使用remove功能,和unlink同样
  • 对于目录,可使用rmdir功能,和unlink同样

6. 符号连接

符号连接是指向一个文件的间接指针。

6.1 符号连接是为了避开硬连接的一些限制

  • 硬连接要求连接和文件位于同一文件系统中
  • 只有超级用户才能建立指向目录的硬连接

6.2 使用符号连接须要注意的事情

  • 当调用某个函数时,须要注意函数处理的是连接的文件,仍是连接自己

6.3 符号连接相关的函数

  • 建立符号连接: symlink
  • 打开符号连接:readlink

7. 目录

  • 建立目录:mkdir
  • 删除目录:rmdir。入炉连接计数为0,且没有进程打开次目录,释放目录空间。
  • 读取目录:
  • 更改当前工做目录:chdir,fchdir

五. 标准IO

  • 标准io库不只在unix上,不少操做系统上都实现了。
  • 标准io处理不少细节,例如:缓冲区分配,优化长度执行io等。便于用户使用。
  • 使用的头文件为<stdio.h>。
  • 标准io的底层调用了前面介绍的unix内核io。
  • 标准io的缺点是效率低。这与它须要复制的数据量有关

1. 流和File对象

  • unix内核io提供的io函数都是针对文件描述符的
  • 可是标准io的操做是针对流进行的
  • 标准io文件流可用于单字节或宽字节字符集,由流定向决定(fwide函数)。
  • 标准io打开一个文件(fopen函数)时,返回一个FILE的指针,它包含了实际io的文件描述符,指向用于该流缓冲区的指针,缓冲区长度,缓冲区当前字符数,出错标志,文件结束标志等信息
  • 每一个进程预约义三个流:标准输入,标准输出,标准出错

2. 缓冲

2.1 缓冲类型

标准io提供三种类型的缓冲

  • 全缓冲:填满标准io缓冲区后才进行实际的io操做(malloc申请缓冲区,flush执行写操做)。
  • 行缓冲:输入输出中遇到换行符时进行实际的io操做。涉及终端设备时,一般用行缓冲。
  • 不带缓冲:不对字符进行缓冲存储。标准出错流一般不带缓冲。

2.2 设置缓冲类型

  • setbuf
  • setvbuf:第三个参数:
    • _IOFBF:全缓冲
    • _IOLBF: 行缓冲
    • _IONBF:无缓冲

3. 打开流

  • fopen:打开一个指定的文件
  • freopen:将一个文件读到一个指定的流。若是流已经打开,就先关闭,已经定向,就先清除定向。
  • fdopen:经过文件描述符打开文件。由于管道和网络通讯等特殊文件不能用标准io函数fopen打开,因此用到该函数。
  • type:指定文件的打开方式

4. 读和写流

读写流有三种不一样的方式

  • 每次读写一个字符:
    • 读:getc,fgetc,getchar
    • 写:putc,fputc,putchar

    不带f前缀的从标准输入流读取数据,带f前缀的从指定流读取数据。不带f前缀的函数不推荐使用,由于它不指定缓冲区大小,会致使溢出。

  • 每次读写一行:
    • 读:gets,fgets
    • 写:puts,fputs
  • 每次读写必定数量的对象(直接io,二进制io):
    • 读:fread,须要指定要读取的元素个数和每一个元素的大小
    • 写:fwrite
    • 缺点:不一样系统间,交换二进制数据会编译期和计算机体系结构不一样而有差别,因此必须用更高级的协议。

5. 定位流

定位标准io流有三种不一样的方式

  • ftell(获取),fseek(设置):long类型的文件位置
  • ftello和fseeko:off_t类型的文件位置
  • fgetpos和fsetpos:fpos_t的抽象数据类型表示文件位置

6. 格式化io

6.1 格式化输出

  • printf:格式化数据写到标准输出
  • fprintf:格式化数据到指定流
  • sprintf:格式化的数据送入数组buf中,尾部自动加入null。可能会致使缓冲区溢出,需调用者本身保证
  • 转换说明以%开始

6.2 格式化输入

六. 系统数据文件

1. 口令文件

  • 存放目录:/etc/passwd
  • 数据结构:<pwd.h>中的passwd结构体
  • 查看指定用户口令的函数接口:
  • 查看全部用户口令的函数接口:

2. 阴影文件(加密口令)

  • 存放目录:/etc/shadow
  • 查看的接口:

3. 组文件

  • 存放目录:/etc/group
  • 数据结构:<grp.h>中的group
  • 查看指定组:
  • 查看全部组:

4. 其余数据文件

  • 服务器提供服务的数据文件:/etc/services
  • 记录协议信息的数据文件:/etc/protocols
  • 记录网络信息的数据文件:/etc/networks

5. 登录帐号文件

  • 当前登录进系统的用户:/var/run/utmp
  • 跟踪登录和注销信息:/var/log/wtmp

6. 获取系统信息

  • 获取主机与操做系统相关信息
  • 只获取主机名

7. 时间格式

  • 日历时间(UTC时间)
  • 更高精度的时间
  • 各类时间的转化关系
相关文章
相关标签/搜索