APUE读书笔记(一):Unix基础知识、文件I/O

由于对linux后台开发方向的向往,在网上研读了各位大神的blog之后,决定拜读APUE,之后再研究UNP。现在开始记录APUE的学习笔记。

第一章 UNIX基础知识

1.errno、strerror和perror
errno就是整形值,各个值代表着不同的错误;errno有两点要注意:第一,如果没有错,其值不会被清除;第二,任何函数都不能将errno置零,且没有任何errno错误信息被定义为0。
strerror定义如下:

#include <string.h>
char* strerror(int errnum);

perror定义如下:

#include <stdio.h>
void perror(const char *msg);

为什么perror的参数要是const,而strerror不用呢?
因为因为strerror传的本就是int型变量,是值传递,不会改变原来的值,而perror传入的是指针,这里为了防止改变这个指针。
下面看看他们的使用:

fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
errno = ENOENT;
perror(argv[0]);

2.终端键盘上的两种产生信号的方式:
1)中断键(Delete键或Ctrl+C)和退出键(Ctrl+\)
Ctrl+D:发送一个exit()信号
Ctrl+C:强制退出进程
Ctrl+Z:挂起进程
2)kill函数

3.系统调用和库函数的区别:
1.应用程序既可以调用系统调用,又可以调用库函数;而有些库函数也是调用的系统调用。
比如:printf库函数会调用write系统调用;malloc函数调用sbrk()系统调用(sbrk()是unix系统调用中处理存储空间管理器);但是strcpy和stoi就不会调用任何内核的系统调用。
2.因为系统调用偏底层,所以它只提供一种最小接口,功能肯定没有库函数提供的复杂啦。

第二章 Unix标准及实现

此处省略一万字。。。

第三章 文件I/O

本章里讨论的I/O都是不带缓冲区的,这里要强调一下:不带缓冲区并不是整个过程都没有缓冲区的参与,因为我们经过内核的时候肯定是要经过内核的缓存区高速缓存的,这里的不带缓冲区是指用户进程中没有相关的缓存机制,仅仅是每次调用这些函数就会调用相应的系统调用。
1.文件描述符
0——STDIN_FILENO
1——STDOUT_FILENO
2——STDERR_FILENO
现在很多系统的文件描述符上限是63个,而linux3.2则是无限的。。仅受到存储器容量、整型字长以及管理员配置的软限制和硬限制的约束。

2.open和openat
openat就是为了解决open的两个问题:
1)可以使用相对路径名打开文件,而不仅仅打开当前路径或绝对路径;
2)避免TOCTTOU错误
这里介绍一下TOCTTOU错误。该类错误指的是,程序是非常脆弱的(vulnerable)如果该程序调用了两个文件相关的函数,第二个函数依赖于第一个函数的结果。因为两个函数是非原子操作,被操作的文件可能被两个函数轮流操作(线程切换),导致第一个函数的结果出错,从而程序出错。

3.creat函数
他只能以只写的方式打开文件,所以现在已经被open取代了

4.lseek函数
这里要注意的是:
lseek仅将文件偏移量记录在内核中,它不引起任何I/O操作
我们还可以通过lseek创建一个“空洞”文件,并且这些原文件尾端和新开始写位置之间的“空洞”是不在磁盘上占用空间的。(这里可以用:od -c filename 的方式按字符打印,查看实际内容)

5.I/O效率
linux ext4文件系统的磁盘块大小是4k,所以我们在read/write的时候将BUFSIZE设为4k性能上就几乎最优了,继续加大BUFSIZE大小也不会提升性能了。
值得注意的是:我们每次测试的时候都采用的不同的BUFSIZE,由于不同缓冲区长度的各次运行使用不同的文件副本,所以不会再前一次运行的高速缓存中找到要的数据,这样就保证了测试的准确性。

6.文件共享
打开文件的内核数据结构:
三级表项:进程表项、文件表项、节点表项
这里写图片描述

7.原子操作
1)打开文件的时候设置O_APPEND标志,这样每次操作前都将进程的当前偏移量设置到该文件的尾端处,也就不用使用lseek了。
2)pread和pwrite函数:
他们可以原子性的执行I/O,相当于先lseek后再执行read/write。但他们也当注意的是:调用pread/pwrite函数时,无法中断他们的操作,也不会更新当前文件的偏移量

8.dup和dup2
首先看他们的定义:

#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);

其实dup函数的作用就是复制一个fd的描述符并返回;儿dup2的作用是将fd2的文件描述符重定向到fd中去。下面我们看一个例子:

dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);

假设fd=1,这三行代码的意思就是,将标准输入、标准输出、标准错误全都重定向到了标准输出中去了!
dup和dup2函数很重要的!

9.sync、fsync和fdatasync函数
由于延迟写的存在,当内核需要重用缓冲区的时候,要将延迟写的数据块写入磁盘中去,为了保证磁盘与缓存区的内容的一致性,引入了这几个函数。看看他们的定义:

void sync(void);
int fsync(int fd);
int fdatasync(int fd);

其中sync是将所有延迟写的块缓冲区放入队列中就返回;而另外两个则是写完才返回,(我的理解就像是sendMessage和postMessage的区别哈),所以后面两个应用于dateBase中。而系统的守护进程周期性的调度sync函数,这样就保证了定期冲洗内核的块缓冲区的作用,另外,命令sync也调用的是sync函数。

10.fcntl函数
这个函数可以修改已经打开的文件的属性,例如:
fcntl(fd, F_DUPFD, 0); 的作用就相当于 dup(fd);
这个函数也被用于记录锁。
这里有个技巧,在命令行中:
5<>filename
表示在文件描述符上以可读、写的方式打开filename文件。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值