《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

  

时间: 2024-10-30 18:36:21

《C程序设计语言》笔记 (八) UNIX系统接口的相关文章

python程序设计语言笔记 第一部分 程序设计基础

1.1.1中央处理器(CPU) cpu是计算机的大脑,它从内存中获取指令然后执行这些指令,CPU通常由控制单元和逻辑单元组成. 控制单元用来控制和协调除cpu之外的其他组件的动作. 算数单元用来完成数值运算(加减乘除)以及逻辑运算(比较) 现在的cpu都是镶在一块小小的硅半导体芯片上,这块芯片上有数百万个被称作晶体管的小电子开关来处理信息. 每台计算机都有一个内部时钟,该时钟会以一个稳定的速度发射电子脉冲,这些脉冲用于同步和控制各种操作的的步调.时钟速度越快,给定时间内执行的指令就越多.时钟速度

C程序设计语言笔记2017/3/25

1.3 for语句 for语句的基本格式如下: for(初始化部分:条件部分:增加步长部分) { 循环体 } for语句是一种循环语句,是对while语句的推广,只是for语句的操作更直观一些.for后面的圆括号共包含3个部分,各部分用分号隔开.当循环体部分只要一条语句时,大括号也可以不要. 具体见下面温度转换程序: #include <stdio.h> /*打印华氏温度-摄氏温度对照表*/ main() { int fahr; for(fahr=0;fahr<=300;fahr=fah

关于unix系统接口 普通文件io的小结

首先是常用的几个函数  open , read,write,lseek, close open函数  函数原型  int open(char *  path,int  oflag,...)           返回值是一个文件描述符   path顾名思义就是文件名  oflage文件是打开方式   第三个形参应用于创建文件时使用  /*创建文件其实还有一个create函数使用  以及openat由于还未使用过这个函数和open的差异 所以不在此处累赘*/          open函数  使用i

《javascript高级程序设计》笔记八

第五章 引用类型(四) 对于我们开发人员来说,JavaScript有种引用类型一定很陌生!那就是基本包装类型:Boolean.Number和String.这也不是我们的错,主要这些我们平时根本都用不到.这些都是JavaScript内部自动调用.这么说,你可能有点懵.下面,我来举个例子. 1 var s1 = "hello,world"; 2 var s2 = s1.substring(1); 3 console.log(s2); //"ello,world" 对于上

c程序设计语言笔记001

统计输入行数 # include<stdio.h> main () { int c,nl; nl=0; while ((c=getchar())!=EOF) { if(c=='\n') ++nl; } printf("%d\n",nl); } 单词计数 统计输入的函数nl 单词数nw 字符数nc # include<stdio.h> //单词计数 统计输入的函数nl 单词数nw 字符数nc #define IN 1 #define OUT 0 main () {

《C程序设计语言》笔记 (一) 序及引言

C语言最早是由Dennis Ritchie于1973年设计并实现的,其初衷是作为UNIX系统的编程语言. <The C Programming Language>一书1978年出版第一版. 1983年,美国国家标准协会(ANSI)成立了一个委员会,其目标是制定"一个无歧义性的且与具体机器无关的以语言定义",而同时又要保持C语言原有的"精神",产生了C语言的ANSI标准. C语言是一种通用的语言.它同UNIX系统之间具有非常密切的联系--C语言是在UNIX

重读《C程序设计语言》(1):序言

之前已经读过一遍K&R的<C程序设计语言>了,但是并没有写读书笔记.这次想再认认真真地读一遍被誉为C语言圣经的<C程序设计语言>.正如书中所说,C语言并不是一种大型语言,也不需要一本很厚的书来描述.所以这次读书笔记也希望采用简洁的方式来记录C语言中的重要知识点. (1)C语言最早是由Deninis Ritchie于1973年设计并实现的. (2)C语言是在UNIX系统上开发的,并且无论是UNIX系统本身还是运行在其上的大部分程序,都是用C语言编写的. (3)由于C语言适合用

程序设计语言的定义及一般特征

1.程序语言的定义 程序设计语言是一个记号系统.记号系统有两个特征: 语法:语言的一组规则,用来形成和产生程序 语义:语言的意思,用来表示程序的逻辑关系 2.语法相关的一些定义 字母表:元素的非空有限集,记为∑.例如:∑={a,b} 符号:字母表中的元素称之为符号 符号串:符号的有穷序列,例如:a,aa,aaa,ac,aaccc,..,无任何符号的符号串称为空符号串,记为ε 符号串长度:符号串中符号个数,若有x=sss,则x的长度为|x|=3,|ε|=; 符号串连接:若x.y是定义在∑上的符号串

C++语言笔记系列之十八——虚函数(1)

1.C++中的多态 (1)多态性:同一个函数的调用可以进行不同的操作,函数重载是实现多态的一种手段. (2)联编:在编译阶段进行联接,即是在编译阶段将一个函数的调用点和函数的定义点联接起来. A.静态联编:在编译阶段就完成的函数联编--函数重载. B.动态联编:在程序的运行阶段由系统自动选择具体的函数--虚函数. 注:C++的多态主要指的就是动态联编. 2.虚函数 (1)虚函数是在函数的定义时将其声明为虚函数即可. (2)说明:virtual 数据类型 函数名(参数表) {函数体} A.目的:当