zoukankan      html  css  js  c++  java
  • 《C程序设计语言》笔记 (八) UNIX系统接口

    8.1 文件描述符

    UNIX操作系统通过一系列的系统调用提供服务,这些系统调用实际上就是操作系统内的函数
    
    ANSI C标准函数库是以UNIX系统为基础建立起来的
    
    在UNIX系统中所有的外围设备都被看作是文件系统中的文件,因此,所有的输入输出都要通过文件读写完成
    
    也就是说,通过一个单一的接口就可以处理外围设备和程序之间的所有通信
    
    在读写文件之前,必须先将这个意图通知系统,该过程称为打开文件
    
    如果是写写一个文件,则可能需要先创建该文件
    
    操作系统想程序返回一个小的非负整数,该整数称为文件描述符。
    
    任何时候对文件的输入输出都是通过文件描述符标识文件,而不是通过文件名标识文件
    
    系统负责维护已打开文件的所有信息,用户程序只能通过文件描述符引用文件
    
    因为大多数的输入输出都是通过键盘和显示器来实现的
    为了方便起见,UNIX对此做了特别的安排
    当命令解释程序运行一个程序的时候,它将打开3个文件
    对应的文件描述符分别是012 依次标识标准输入 标准输出 标准错误
    

      

    8.2 低级IO --read和write

    输入输出是通过read和write系统调用实现的
    
    在C语言中,可以通过函数read和write访问这两个系统调用
    
    read(int fd,char *buf,int n);
    
    write(int fd,char *buf,int n);
    
    第一个参数是文件描述符
    
    第二个参数是程序中存放读写的数据的字符数组
    
    第三个参数是要传输的字节数
    
    没个调用返回实际传输的字节数
    
    在读文件时,函数的返回值可能会小于请求的字节数
    
    如果返回值为0,则标识已到达文件结尾
    
    如果返回-1则表示发生了某种错误
    
    
    在写文件时返回值是实际写入的字节数
    
    如果返回值与写入的字节数不相等,则说明发生了错误
    
    在一次调用中,读写的数据可以为任意大小
    
    最常用的值为1,即每次读写1字节(无缓冲)
    
    或是类似于1024或4096这样的与外围设备的物理块大小相应的值
    
    用更大的值调用该函数可以获得更高的效率,因为系统调用的次数减少了
    
    #include "syscalls.h"
    
    main()
    {
       char buf[BUFSIZ];
       int n;
    
       while((n = read(0,buf,BUFSIZ)) > 0)
          write(1,buf,BUFSIZ);
       return 0;
    }
    
    系统调用的函数原型集中放在一个syscalls.h中
    
    参数BUFSIZ也已经包含中头文件中
    
    对应所使用的操作系统来说,该值是一个较合适的数值
    
    如果文件大小不是BUFSIZ的倍数,则对read的某次调用会返回一个较小的字节数
    
    //getchar实现
    
    int getchar(void)
    {
       char c;
       return (read(0,&c,1)==1)?(unsigned char) c : EOF;
    }
    
    其中c必须是一个char类型的变量,因为read函数需要一个字符指针类型的参数
    
    在返回语句中将c转换为unsigned char类型可以消除符号扩展问题
    
    如果要在包含头文件<stdio.h>的情况下编译getchar函数
    
    就必须用#undef预处理指令取消getchar的宏定义
    
    因为在头文件中,getchar是以宏方式实现的
    

      

    8.3 open creat close 和unlink

    除了默认的标准输入 标准输出和标准错误文件外
    
    其他文件都必须在读写前显式打开
    
    系统调用open和creat用于实现该功能
    
    open和fopen很相似,不同的是 
    
    open非常一个文件描述符 int类型的数值
    
    fopen返回一个文件指针
    
    #include <fcntl.h>
    
    int fd;
    
    fd = open(char *name,int flags,int perms);
    
    参数name文件名
    
    flags是一个int类型的值,说明文件打开方式 
    包括
        
         O_RDONLY  只读
        O_WRONLY  只写
        O_RDWR    读写
    
    目前 open 参数perms的值始终为0;
    
    如果打开的文件不存在则将导致错误
    
    
    可以使用creat系统调用创建新文件或覆盖已有的文件
    
    int fd;
    fd = creat(char *name,int perms);
    
    如果创建成功返回文件描述符 否则返回-1
    
    如果此文件已存在,清空原来数据
    
    使用creat创建一个已经存在的文件不会导致错误
    
    如果要创建的文件不存在,以参数perms指定的权限创建文件
    
    每个文件对应一个9比特的权限信息
    
    所有者
    所有者组
    其他成员
    
    一个程序同时打开的文件数是有限制的(通常为20)
    
    如果一个程序需要同时处理许多文件,那么他必须重用文件描述符
    
    函数close用了断开文件描述符和已打开文件直接的连接,并释放此文件描述符
    
    close函数和标准库中的fclose函数想对应,但它不需要清洗(flush)缓冲区
    
    如果程序通过exit函数退出或从主程序中返回,所有打开的文件将被关闭
    
    函数unlink将文件从文件系统中删除,它对应标准库函数remove
    

      

    8.4 随机访问 lseek

    输入输出通常是顺序进行的
    
    每次调用read和write进行读写的位置紧跟在前一个操作的位置后
    
    但是,有时需要以任意的顺序访问文件,系统调用lseek可以在文件中任意移动位置而不实际读写任何数据
    
    long lseek(int fd,long offset,int orign);
    
    将文件描述符fd的当前位置设置为offset
    
    offset是相对于origin指定的位置而言
    
    随后进行的读写操作将从此位置开始
    
    使用lseek系统调用时,可以将文件视为一个大数组,其代价是访问速度慢一些
    
    标准库函数fseek和系统调用lseek类似
    
    不同的是,前者的第一个参数是FILE *类型
    
    且发生错误时返回一个非0值
    

      

    8.5 实例 --  fopen和getc函数的实现

    标准库中的文件不是通过文件描述符描述的,而是使用文件指针描述的
    
    文件指针是一个指向包含文件各种信息的结构的指针
    
    包含如下信息:
    
    一个指向缓冲区的指针,通过它可以一次读入文件的一大块内容
    
    一个记录缓冲区中剩余的字符数的计数器
    
    一个指向缓冲区下一个字符的指针
    
    文件描述符
    
    描述读写模式的标志
    
    描述错误状态的标志
    
    描述文件的数据结构包含在<stdio.h>中
    
    只供标准库中其他函数使用的名字以下划线开始,因此一般不会和用户程序中的名字冲突
    

      

    8.6 实例 -- 目录列表

    在UNIX中的目录就是一种文件
    
    ls只需读取此文件就可以获得所有的文件名
    
    但是如果需要获取文件的其他信息,就需要系统调用
    

      

    8.7 实例 -- 存储分配程序

    malloc在必要是调用系统以获取更多的存储空间
    
    malloc并不是从一个在编译时就确定的固定大小的数组中分配存储空间
    
    而是在需要的时候想操作系统申请空间
    
    因为程序中的某些地方可能不通过malloc申请空间
    
    所以malloc管理的空间不一定是连续的
    
    这样空闲存储空间以空闲块链表的方式组织
    
    每个块包含一个长度 
    
    一个指向下一块的指针及一个指向自身存储空间的指针
    
    当有申请请求时,malloc将扫描空闲块链表,直到找到一个足够大的块为止
    
    该算法称为 "首次适应"
    
    与之对应的算法是"最佳适应"
    
    如果块恰好与请求的大小符合
    
    将它从链表中移走并返回给用户
    
    如果块太大,则将它分成两部分:
    
    大小合适的返回给用户,剩下的部分留在空闲链表中
    
    如果找不到足够大的块,则想操作系统申请一个大块并加入到空闲链表中
    
    释放过程也是首先搜索空闲链表
    
    以找到可以插入被释放块的合适位置
    
    如果与被释放块相邻的任意一边是空闲,则将两块合并成一个大块
    
    由malloc返回的存储空间满足将要保存的对象的对齐要求
    
    虽然机器类型各异,但是每个特定的机器都有一个最受限的类型
    
    如果最受限的类型可以存储在某个特定的地址中
    
    则其他所以的类型也可以存放在此地址中
    
    向系统申请存储空间是一个开销很大的操作
    
    我们不希望每次malloc执行该操作
    
    UNIX系统调用sbrk(n)返回一个指针,该指针指向n个字节的存储空间
    
    如果没有空闲空间,sbrk返回-1 
    

      

  • 相关阅读:
    Interview with BOA
    Java Main Differences between HashMap HashTable and ConcurrentHashMap
    Java Main Differences between Java and C++
    LeetCode 33. Search in Rotated Sorted Array
    LeetCode 154. Find Minimum in Rotated Sorted Array II
    LeetCode 153. Find Minimum in Rotated Sorted Array
    LeetCode 75. Sort Colors
    LeetCode 31. Next Permutation
    LeetCode 60. Permutation Sequence
    LeetCode 216. Combination Sum III
  • 原文地址:https://www.cnblogs.com/dafume/p/7813008.html
Copyright © 2011-2022 走看看