第三章 系统编程概念
- 与用户空间函数调用相比,最简单的系统调用会产生比较大的打开,因为为了执行系统调用,系统需要临时切换到核心态,发生中断,此外,内核还需要验证系统调用的参数,用户内存和内核内存之间的数据需要传递
// 如果代码里有pidof()需要,可以换一种方式实现
while true:
do
local pid_s = `pidof super`
fi
sleep 5
done
// 改进的程序
local pid_s = `pidof super`
while true:
do
if [ -z $pid_s ] || [ ! -d /proc/$pid_s ]; then
do something
fi
sleep 5
done
第五章 文件io
5.1 原子操作和竞争条件
如果一个进程执行到lseek()和write()之间,被执行相同代码的第二个进程中断,那么这两个进程会在写入数据前,将文件偏移量设为相同位置,而当一个进程再次获得调度时,会覆盖第二个进程已写入的数据(脏数据)。
if (lseek(fd, 0, SEEK_END) == -1)
errExit("lseek");
if (write(fd, buf, len) != len)
fatal("Partial/failed write");
规避这一问题,可以在打开文件加入O_APPEND标志。
- 文件特定偏移量处的IO:pread和pwrite,具有原子性- 分散输入和集中输出,readv() writev(),也属于原子操作
- preadv(), pwritev(), 增加了offset参数
- truncate() ftruncate, 可以将文件大小设为length参数指定值
- 非阻塞io, O_NONBLOCK
- 大文件io,2gb内,fopen64(), open64(), lseek64(), truncate64()
- /dev/fd 内核给每个进程提供一个特殊的虚拟目录/dev/fd
- 创建临时文件,mkstemp, tmpfile
第六章 进程
进程pid
- 进程pid,最大是32767,如果超过,内核将进程号计数器重置为300(因为300以内通常是系统进程,为了减少没必要的检索)。可以通过/proc/sys/kernel/pid_max修改pid最大阈值
第七章 内存分配
内存一些错误
- 分配内存后,不要改变这块内存范围外的内容
- 释放同一块内存,会报sigsegv信号
- 没有经过malloc函数返回的指针,不能调用free
- 长时间运行的程序,如果反复分配内存,应确保用完就释放
内存调试的工具和库
- mtrace() muntrace() 开发或关闭对内存分配调用进行跟踪功能
- mcheck() mprobe() 允许程序对分配内存进行一致性检查
- MALLOC_CHECK_ 环境变量提供了类似mcheck() 和mprobe()函数的功能
在堆上分配内存其他方法
- calloc 对已分配的内存初始化为0
- realloc 增加已分配内存的大小,不会初始化
- memalign() posix_memalign() 分配内存时,起始地址要与2的整数次幂对齐
在堆栈上分配内存 alloca()
动态分配内存,通过增加栈帧大小从堆栈上分配
缺点:堆栈溢出时,无法预知,没有返回NULL
优点:1. 分配内存速度比malloc快,因为编译器将alloca()作为内联代码处理; 2. alloca分配的内存随栈帧的溢出自动释放
第13章 文件IO缓冲
13.1 缓冲区大小对io系统调用性能影响
复制100m大小的文件所需要的时间
buf_size | 总用时 | 总cpu用时 | 用户cpu用时 | 系统cpu用时 |
---|---|---|---|---|
1 | 107.43 | 107.32 | 8.20 | 99.12 |
– | – | – | – | – |
128 | 2.16 | 1.59 | 0.11 | 1.48 |
– | – | – | – | – |
4096 | 2.05 | 0.38 | 0.01 | 0.38 |
65535 | 2.06 | 0.32 | 0.00 | 0.32 |
- 缓冲区大小1个字节时,调用read() wirte()一亿次。
- 缓冲区大小4096个字节时,需要调用read() write() 24000次左右。
- 如果缓冲区大小超过4096这个值,性能提升就不会很显著了,因为用户空间和内核空间之间复制数据以及执行实际磁盘IO所花费的时间相比,read 和 write系统调用成本就微不足道了
- 总之,文件发生大量数据传输时,可以采用大块空间缓冲数据,执行更少的系统调用
13.2 stdio缓冲
- setvbuf() 控制stdio库使用缓冲的形式
- fflush() 刷新缓冲区
fflush(stdout);
// 如果stdout是已写打开的文件的指针,则将输出缓冲区的内容写入该指针指向的文件
// 否则清除输出缓冲区
13.3 控制文件io的内核缓冲
强制刷新内核缓冲区到输出文件有时是很有必要的(比如要确保继续操作前将输出真正写入磁盘)
- fsync() 使缓冲数据和打开fd相关所有元数据刷新到磁盘上
- fdatasync() 减少对磁盘操作的次数
- sync() 更新文件信息的所有内核缓冲区刷新到磁盘上
第15章 文件属性
获取文件信息stat()
struct stat
{
dev_t st_dev; //device 文件的设备编号
ino_t st_ino; //inode 文件的i-node
mode_t st_mode; //protection 文件的类型和存取的权限
nlink_t st_nlink; //number of hard links 连到该文件的硬连接数目, 刚建立的文件值为1.
uid_t st_uid; //user ID of owner 文件所有者的用户识别码
gid_t st_gid; //group ID of owner 文件所有者的组识别码
dev_t st_rdev; //device type 若此文件为装置设备文件, 则为其设备编号
off_t st_size; //total size, in bytes 文件大小, 以字节计算
unsigned long st_blksize; //blocksize for filesystem I/O 文件系统的I/O 缓冲区大小.
unsigned long st_blocks; //number of blocks allocated 占用文件区块的个数, 每一区块大小为512 个字节.
time_t st_atime; //time of lastaccess 文件最近一次被存取或被执行的时间, 一般只有在用mknod、utime、read、write 与tructate 时改变.
time_t st_mtime; //time of last modification 文件最后一次被修改的时间, 一般只有在用mknod、utime 和write 时才会改变
time_t st_ctime; //time of last change i-node 最近一次被更改的时间, 此参数会在文件所有者、组、权限被更改时更新
};
access 检查对文件的访问权限
常量 | 描述 |
---|---|
F_OK | 有这个文件 |
R_OK | 有读权限 |
W_OK | 有写权限 |
X_OK | 有执行权限 |
更改文件权限 chmod() fchmod()
18章 目录和链接
unlink() 移除一个链接
执行unlink()函数并不一定会真正的删除文件,它先会检查文件系统中此文件的连接数是否为1,如果不是1说明此文件还有其他链接对象,因此只对此文件的连接数进行减1操作。若连接数为1,并且在此时没有任何进程打开该文件,此内容才会真正地被删除掉。在有进程打开此文件的情况下,则暂时不会删除,直到所有打开该文件的进程都结束时文件就会被删除。