1.UNIX基础知识
1.基本知识
- 程序调用内核的入口叫做系统调用
- GNU是一个类Unix操作系统,典型的内核就是Linux
- 用户登录信息存储于/etc/passwd
- Bourne-again shell就是bash
- 根目录的起点是"/"
2.文件和目录
- 根目录的名称是"/"
- 斜线和空格不能出现在文件名中
- 登录时的工作目录从/etc/passwd中获取
3.输入和输出
- 内核(kernel)利用文件描述符(file descriptor)来访问文件,不同进程文件描述符
相同,那么可能是同一个文件,也有可能不是 - 每当运行一个新程序时,所有shell都为其打开3个文件描述符,它们三个都是链接向终端
- 标准输入
- 标准输出
- 标准错误
- 不断缓冲的IO open read write lseek close
- 标准IO 使用标准IO无需担心如何选择最佳缓冲区的大小。比如printf
4.程序和进程
- 程序是存储在磁盘某个目录上的可执行文件
- 进程控制的主要函数
- fork创建一个新进程
- exec 执行
- waitpid 父进程等待子进程终止,通过waitpid实现
- 线程ID只在它所属的进程内起作用
5.出错处理
UNIX系统函数出错时,通常会返回一个负值
错误分为致命性错误和非致命性错误
6.用户标识
- 用户ID 用户不能更改,0为根用户
- 组ID 数值,组文件时/etc/group
- 附属组ID
7.信号
用于通知进程发生了某种状况,进程可以对信号做三种处理
- 忽略信号
- 默认方式处理
- 信号处理函数
信号产生的方式多种多样,比如Ctrl+C,kill函数等等
8.时间
时间分为两种,日历时间(就是时间戳)和进程时间
进程时间又分为:
- 时钟时间,进程运行时间总量
- 用户CPU时间,执行用户指令所用的时间量
- 系统CPU时间,执行内核程序所经历的时间
9.系统调用和库函数
操作系统提供多种服务的入口点,由此程序向内核请求服务
UNIX所使用的技术是为每个系统调用在标准C库中设置一个具有同样名字的函数,用户进程调用这些函数,然后函数用系统所要求的技术调用对应的内核服务
系统调用和库函数的区别
- 从用户角度来看,它们区别并不重要
- 我们可以替换库函数,但是系统调用通常不能替换
- 应用程序既可调用系统调用也可以调用库函数,很多库函数还会调用系统调用
- 系统调用一般是最小接口,库函数提供比较复杂的功能
- fork exec wait通常由用户应用程序直接调用
标准化和实现
1.标准化
⑴ISO C
意图是提供C程序的可移植性,ISO C头文件依赖于操作系统所配置的C编译器版本
⑵POSIX
它定义了符合POSIX标准的操作系统必须提供的各种服务,它也包含了ISO C标准库函数,它的全名叫可移植的操作系统接口
ISO C标准如果和POSIX.1之间冲突,POSIX.1服从ISO C
POSIX标准分为必须部分和可选部分,必须不含如下:
⑶Single UNIX Specification(SUS)
POSIX接口的超集,POSIX相当于它的基本规范,只有遵循SUS标准的操作系统才能称作UNIX
2.主要实现
ISO C,POSIX,SUS只是接口的规范,主要的实现如下
- BSD 伯克利分校开发的
- Linux 类似于UNIX的操作系统
- Mac OS X 内核也是一个UNIX系统
- Solaris Sun公司开发的UNIX系统
这四种只有Mac OS X和 Solaris 10是UNIX系统,但是他们都提供UNIX编程环境,都在不同程度上符合POSIX标准
3.限制(▲)
-
编译时限制,在头文件中定义
-
运行时限制,要求进程调用一个函数获得限制值
- 与文件和目录无关的运行时限制(sysconf函数)
- 与文件或目录有关的运行时限制(pathconf和fpathconf函数)
如果一个特定的运行时限制啊在一个给定的系统中不改变,可以静态的定义在一个头文件中,如果没有定义在头文件中,应用程序就必须调用3个conf函数中的一个
⑴ISO C函数
所有编译时限制都列在头文件limits.h中
⑵POSIX限制
POSIX对ISO C进行了扩充
⑶三个运行时限制函数
若成功,返回相应值,出错返回-1
①sysconf
②pathconf
参数是路径名
③fpathconf
参数时文件描述符
⑷不确定的运行方式限制
- 路径名
- 最大打开文件数
4.选项
如果我们要编写一些可移植的应用程序,而这些程序与所有得到支持的选项有关,那么就需要一种可移植到方法以决定一种实现是否支持一个给定的选项
- 编译时选项定义在<unistd.h>中。
- 与文件或目录无关的选项用sysconf函数确定。
- 与文件或目录有关的选项通过调用pathconf或fpathconf函数来发现
* 如果符号常量的定义值为-1,那么该平台不支持相应的选项。
- 如果符号常量的定义值大于0,那么该平台支持相应的选项。
- 如果符号常量的定义值为0,则必须调用sysconf、pathconf或fpathconf以确定相应的选项是否受到支持
5.功能测试宏
头文件有POSIX.1和XSI的符号,也有具体实现自己的定义,为了防止和实现冲突,我们可以通过定义功能测试宏的方式来使用POSIX.1或者XSI的定义
6.基本系统数据类型
基本系统数据类型定义在头文件sys/types.h中,也有很多定义在其它数据类型,他们绝大多数都是_t结尾
文件IO
1.基本知识
- UNIX系统的而大多数文件IO只需要用到open,read,write,lseek,close函数,这些都是不带缓冲的IO,并非标准IO
- 文件描述符
- 0是进程标准输入
- 1是进程标准输出
- 2是进程标准错误
- 大多数现代文件系统文件名最大长度255
2.open/openat/close
若成功,返回文件描述符,出错返回-1
- open
- openat 相对于open,它可以使用相对路径打开目录中的文件
返回的文件描述符一定是最小的未用描述符数值
- close 关闭文件
3.creat
创建一个新文件,成功返回一个文件描述符,出错返回-1
可以被open函数替代,而且open函数可以以读写的方式打开文件,这样在创建临时文件时更方便。creat只能以只写的方式打开
4.lseek
读写操作都是从当前文件偏移量开始,打开文件除非是O_APPEND,否则是0
我们可以使用lseek给一个打开文件设置偏移量
管道,FIFO,网络套接字无法设置偏移量,我们可以用lseek来检查是否可以设置,不能设置的返回-1
设置偏移量大于文件当前长度,下次写时会形成全为0的空洞,空洞不占用磁盘存储区
5.read/write
-
read 返回值,读到的字节数,若已经到文件尾,返回0,若出错,返回-1
-
write 返回值,成功返回已写的字节数,若出错,返回-1,写操作从文件当前偏移量开始
6.进程文件共享
在Linux中,如果两个进程各自打开了同一文件:
不同的进程的vi节点是共享的,所以多个进程操作同一个文件可能会有问题。
解决方式是将定位和写合并成一个原子操作。UNIX通过O_APPEND标志来设置原子操作。
- pread 相当于lseek后调用read,但是有一定的区别
- pwrite 相当于lseek后调用write,同样有区别
9.dup和dup2
- dup 返回的文件描述符是当前可用文件描述符的最小值
- dup2 可以指定新的文件描述符
复制一个现有的文件描述符,成功返回新的文件描述符,失败返回-1
两个描述符共享相同的内部结构,共享所有的锁定,读写位置和各项权限或flags等等。例如:对一个文件描述符进行了lseek操作,另一个文件描述符的读写位置也会随之改变。不过,文件描述符之间并不共享close-on-exec flags,也就是一个关闭对另一个没有影响
10.sync fsync fdatasync
UNIX系统在内核中设有缓冲区,大多数磁盘IO都通过缓冲区进行,当写数据时,先写缓冲区,然后进入队列,最后进入磁盘
- sync 将缓冲区写入队列后返回
- fsync 只对特定文件描述符起作用,磁盘操作结束返回,确保修改过的块立即写到磁盘
- fdatasync 它只影响文件数据部分,而fsync还会同步更新文件属性
10.fcntl
- 复制一个已有的描述符
- 获取/设置文件描述符标志
- 获取/设置文件状态标志
- 获取/设置异步I/O所有权
- 获取/设置记录锁
11.ioctl
I/O操作杂物箱,很多IO操作都能用ioctl表示,主要用于终端I/O
文件和目录
1.获取文件信息
重点是stat函数,它通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
int stat(const char *file_name, struct stat *buf);
下面是buf的结构体:
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_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; //最后一次改变时间(指属性)
};
2.文件类型
- 普通文件 文本或者二进制