出版社: 东南大学出版社
副标题: LINUX系统编程
译者: O'Reilly Taiwan公司
出版年: 2009-7
页数: 382
定价: 56.00元
ISBN: 9787564115197
内容简介 · · · · · ·
《LINUX系统编程》讲述了:在某些时刻,几乎所有的程序员都要与其程序所处操作系统中的系统调用和程序库打交道。《LINUX系统编程》主要讨论如何编写Linux系统软件——代码位于底层,并且直接跟内核及核心系统程序库对话。《Linux系统编程》描述了使用标准接口包括使用Linux独有的高级接口时,在功能和性能之间如何进行权衡取舍的策略。
该书同样也是一本内行人士编写灵活高效代码的学习指南。作为内核黑客和《LINUX系统编程》的作者,Robert Love不仅阐释了系统接口应该如何工作,还介绍了它们实际上是如何工作的,以及怎样安全有效地使用它们。《Linux系统编程》包含了帮助你在任何层面编写更佳代码的实用技巧。
《LINUX系统编程》主题包括:
读写文件以及其他文件I/O操作,包括Linux内核如何实现和管理文件I/O,内存映射与优化
技术进程管理的系...
《LINUX系统编程》讲述了:在某些时刻,几乎所有的程序员都要与其程序所处操作系统中的系统调用和程序库打交道。《LINUX系统编程》主要讨论如何编写Linux系统软件——代码位于底层,并且直接跟内核及核心系统程序库对话。《Linux系统编程》描述了使用标准接口包括使用Linux独有的高级接口时,在功能和性能之间如何进行权衡取舍的策略。
该书同样也是一本内行人士编写灵活高效代码的学习指南。作为内核黑客和《LINUX系统编程》的作者,Robert Love不仅阐释了系统接口应该如何工作,还介绍了它们实际上是如何工作的,以及怎样安全有效地使用它们。《Linux系统编程》包含了帮助你在任何层面编写更佳代码的实用技巧。
《LINUX系统编程》主题包括:
读写文件以及其他文件I/O操作,包括Linux内核如何实现和管理文件I/O,内存映射与优化
技术进程管理的系统调用,包括实时进程
文件与目录——创建、移动、复制、删除和管理
内存管理——内存分配接口,管理内存,以及优化内存访问
信号及其在Unix系统中的角色,以及基本和高级信号接口
时间、休眠和时钟管理,从基础开始讲述,并且涵盖POSIX时钟和高精度计时器拥有《Linux系统编程》,你将从理论和应用的角度深入了解Linux,可以最大限度地利用系统的潜能。
目录 · · · · · ·
前言
第一章 介绍与基本概念
系统编程
API与ABI
标准
Linux编程的概念
向系统编程迈进
第二章 文件I/O
打开文件
以read()进行读取操作
以write()进行写入操作
同步化I/O
关闭文件
使用lseek()查找文件位置
针对特定位置的读取与写入
截短文件
多任务式I/O
内核内部
结束语
第三章 缓冲式I/O
用户缓冲式I/O
标准I/O
打开文件
经文件描述符打开流
关闭流
从流中读取
使用缓冲式I/O的简单程序
查找一个流
刷新一个流
错误与EOF
取得相应的文件描述符
控制与缓冲机制
线程安全
标准I/O的缺陷
结束语
第四章 高级文件I/O
分散一聚集I/O
事件轮询接口
将文件映射至内存
对一般文件I/0的用法提供建议
同步化、同步及异步操作
I/O调度程序与I/O性能
结束语
第五章 进程管理
进程ID
运行一个新进程
终止一个进程
等待已终止的子进程
用户与组
会话与进程组
守护进程
结束语
第六章 高级进程管理
进程的调度
让出处理器
进程优先级
实时系统
资源限制
第七章 文件和目录管理
文件与其元数据
目录
链接
文件的复制以及移动
设备节点
带外通信
第八章 内存管理
进程地址空间
分配动态内存
管理数据段
匿名内存映射
高级内存分配
调试内存分配
基于堆栈的分配
选择内存分配机制
操作内存
锁定内存
投机取巧的分配策略
第九章 信号
信号的概念
基本的信号管理
发送一个信号
可重人性
信号集
阻挡信号
高级信号管理
以payload送出信号
结束语
第十章 时间
时间的数据结构
POSIX时钟
取得当前时间
设定当前时间
操作时间
调整系统时钟
休眠与等待
定时器
附录GCC对C语言的扩展
参考书目
· · · · · · (收起)
喜欢读"LINUX系统编程"的人也喜欢 · · · · · ·
LINUX系统编程的话题 · · · · · · ( 全部 条 )



LINUX系统编程的书评 · · · · · · ( 全部 8 条 )

翻译是渣,请不要看人民邮电的第二版!


知道自己要做什么才是最重要的
> 更多书评 8篇
-
松鼠亲自奥利奥 (all men are created equal)
进程(process)是执行中的目标码(object code):正在运行的程序。但它不仅仅是目标码,进程由数据、资源、状态以及一个虚拟的计算机组成。 一个进程在内存中比较有意义的有 text、data 和 bbs 三段。text 段包含可执行代码和只读数据(例如常量),RX;data 段中包含已经初始化过的数据(例如已定义初值的变量),RW;bbs 段包含尚未初始化的全局数据。进程通常只通过系统调用来请求和操作资源,其所分配得到的资源会被内核...2012-04-15 20:45:48 2人喜欢
进程(process)是执行中的目标码(object code):正在运行的程序。但它不仅仅是目标码,进程由数据、资源、状态以及一个虚拟的计算机组成。 引自 介绍基本概念 一个进程在内存中比较有意义的有 text、data 和 bbs 三段。text 段包含可执行代码和只读数据(例如常量),RX;data 段中包含已经初始化过的数据(例如已定义初值的变量),RW;bbs 段包含尚未初始化的全局数据。进程通常只通过系统调用来请求和操作资源,其所分配得到的资源会被内核存入进程描述符(process descriptor)。 进程本身是一个虚拟的抽象对象。每个进程由一个或多个线程(threads of execution)构成,而线程则是最小执行单位。传统 Unix 程序惯用每个进程单线程的程序,因为 Unix 简单快速创建进程的机制和稳固的进程间通信(IPC)机制降低了对线程的需求。线程由存放局部变量的堆栈(stack)、处理机状态和目标码执行位置指针组成。进程的其余部分则由进程内的多个线程共享。
Linux 中的授权机制由用户(user)和组(group)提供。每个用户都会被分配一个独一无二的正整数,称为用户标识符(user ID,简称 uid)。每个进程恰好会被关联到一个 uid,用来识别运行进程的用户,这被称为进程的真实用户标识符(real uid)。 引自 介绍基本概念 uid 和 username 的对应存储在 /etc/passwd 中。登录系统时 login 询问用户名和密码,如果有效则衍生(spawn)出 login shell,让 shell 的 real uid 等于登录用户的 uid。子进程的 uid 继承自父进程。uid 的所有权属于 root,只有 root 可以更改进程的 real uid,故 login 必须使用 root 运行。 每个进程除了 real uid 还拥有effective uid(有效用户标识)、saved uid、filesystem uid(文件系统用户标识)。real uid 一般代表启动进程;effective uid 可以更改(以适应权限分配需要),它将决定实际权限;saved uid 保存上一次 effective uid 的值;filesystem uid 决定访问文件系统的权限,一般情况下等于 effective uid。 每个用户可能隶属于一个或多个组(group),包括 /etc/passwd 列出的主要组(primary group)和登录组(login group),以及 /etc/group 中的追加组(supplemental group)。每个进程也会关联到一系列的组标识(group ID,缩写 gid),进程 gid 的种类和进程 uid 的种类对应。进程一般关联到用户的登录组而非追加组。 文件的权限用总共 9 bit 的二进制码表示,其中文件拥有者(owner)权限、拥有者所在组权限、其他用户权限各使用 3 bit ,三个置为为 1 分别代表拥有读取权限(R)、拥有写入权限(W)、拥有执行权限(X)。权限码的每三位可以用十进制表示,例如 777 就表示 owner、owner group、other 都拥有 R、W、X 权限。
回应 2012-04-15 20:45:48 -
松鼠亲自奥利奥 (all men are created equal)
文件 I/O 在 Unix 和 Linux 中都具有重要意义,因为操作系统对所有具有输入输出的对象都以“一切都是文件”的方式来抽象。包括设备文件、管道、目录、futex、FIFO、socket 都可以被抽象为文件来供操作。 一个文件必须先打开,才可以对它进行操作。内核会替每个进程维护一份已打开文件列表,称为文件表(file table)。此表是通过非负整数被索引,而此非负整数称为文件描述符(file descriptor,缩写 fd)。 这一段文字有点费解...2012-04-17 21:01:17 1人喜欢
文件 I/O 在 Unix 和 Linux 中都具有重要意义,因为操作系统对所有具有输入输出的对象都以“一切都是文件”的方式来抽象。包括设备文件、管道、目录、futex、FIFO、socket 都可以被抽象为文件来供操作。
一个文件必须先打开,才可以对它进行操作。内核会替每个进程维护一份已打开文件列表,称为文件表(file table)。此表是通过非负整数被索引,而此非负整数称为文件描述符(file descriptor,缩写 fd)。 引自 文件 I/O 这一段文字有点费解,我查找了其他资料辅助,得到的说法是:文件描述符并非进程内唯一,而是全局唯一存在。对于一个已启动的操作系统,它是跨进程存在的。而 file table 在 Linux Tutorial Glossary 中的描述是:“A table internal to the kernel that maintains the the relationship between the file descriptors and the physical file on the hard disk.” 可见其也是“internal to the kernel”的,是维护文件描述符(fd)和物理文件(可以看成 inode)关系的一张表。
默认情况下,子进程会取得其父进程的文件表。 引自 文件 I/O 在 man fork 中看到了关于文件描述符被子进程继承的说法:“The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two descriptors share open file status flags, current file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).” 可见子进程对一个已打开的文件的属性信息还是会继承的。 综合书中说法,file table 应该指的就是关于已打开文件属性的记录结构:
该表(file table)中每个条目内含与已打开文件相关的信息,包括一个指针(指向文件 inode 在内存中的副本)以及相关的元数据(例如文件位置与访问模式)。 引自 文件 I/O 一个文件描述符可表示成一个整型数据,默认情况下上限为 1024,其中有特殊的三个:0-标准输入-stdin,1-标准输出-stdout,2-标准错误-stderr。 == 打开文件 == 文件控制系统调用的 C API 包含在 fcntl.h 头文件中。 * 打开文件:int open(const char *name, int flags[, mode_t mode]); * flags 至少需要包含 O_RDONLY(read only)、O_WRONLY(write only)或 O_RDWR(read and write)之一,其他参数可以通过与操作符(|)附加。 * O_APPEND 附加模式(位置指针自动指向末尾)。 * O_ASYNC 异步模式,如果文件可读取(可写入)了则产生一个 SIGIO 信号。只能用于 sockets 不能用于常规文件。 * O_CREAT(create 怎么少了一个字母呢)如果文件不存在则自动创建。 * O_DIRECT 直接 I/O,让内核尽量减少 I/O 管理在场机会,跳过内核缓存、缓冲。启用后文件偏移量必须是底层设备的扇区大小整数倍。 * O_DIRECTORY 若不是目录则调用失败。一般不直接使用,而是供 opendir 链接库调用。 * O_EXCL 和 O_CREAT 相反,如果文件已经存在则调用失败。用于避免创建文件时竞争条件(race condition)。 * O_LARGEFILE 以 64 位偏移量打开文件,以便支持打开大于 2G 的文件。64 位架构上是默认值。 * O_NOFOLLOW 不跟踪 symlink 重定向——如果文件是符号链接则调用失败。 * O_NONBLOCK 非阻塞模式,让进程在 I/O 时不会被阻塞(休眠),仅用于 FIFO。 * O_SYNC 同步模式,阻塞 I/O 函数的调用。一般读取默认是同步模式,而写入则采取内核缓冲和 writeback 机制,开启该模式则关闭内核缓冲。 * O_TRUNC 把文件截断成零长度,只对允许写入的常规文件有效,对终端和 FIFO 会被忽略。 == 权限问题 == 默认文件拥有者(owner)就是创建文件进程的 effective uid。 默认文件拥有组(owner group)在 System V(大部分 Linux 的行为模型)中是设定为创建文件进程的 effective gid,在 BSD 中是设定为上层目录的 gid。 在用 open 打开文件的时候如果指定了 O_CREAT 则必须使用 mode 参数指定访问模式。mode 访问模式可以和打开模式抵触,因为打开文件的时候并不会检查 mode。mode 参数就是 Unix 权限位,可以设置为 S_IRWXU(111xxxxxx,owner 可以读写执行)、S_IRUSR(100xxxxxx,owner 可读)、S_IWUSR(010xxxxxx,owner 可写)、S_IXUSR(001xxxxxx,owner 可执行)、S_IRWXG(xxx111xxx,group 可读写执行)、S_IRGRP(xxx100xxx,group 可读)、S_IWGRP(xxx010xxx,group 可写)、S_IXGRP(xxx001xxx,group 可执行)、S_IRWXO(xxxxxx111,other 可读写执行)、S_IROTH(xxxxxx100,other 可读)、S_IWOTH(xxxxxx010,other 可写)、S_IXOTH(xxxxxx001, other 可执行)。权限设置可以使用与操作符来连接。 我觉得去记这么一堆东西实在是很傻的事情,我更愿意直接书写权限码,例如 S_IRWXU | S_IRWXG | S_IXOTH 直接写成 771。 文件操作的实际权限并非直接使用 mode 参数,而是将 mode 与 umask 的补码进行一次与运算,也就是从 umask 中的设置位“拿掉” mode 中的权限。 == 快捷创建新文件、错误处理 == creat(const char* name, mode_t mode) 是 O_WRONLY | O_CREAT | O_TRUNC 组合的快捷方式。 按照惯例,open 和 creat 在失败的时候不返回文件描述符,而是返回 -1,同时 errno 会被设置。 == 读取 == ssize_t read(int fd, void* buf, size_t len); 是读取函数的函数原型,调用时会从“当前”位置开始向后读取 len 个字节,并存入 buf 所指向的地址。读取过程“当前”位置会随之前进,就像迭代器指针一样。 read 函数的读取有以下几种情况: * 实际可用字节数为 0(已到达文件末尾),函数返回 0 表示 EOF。 * 实际可用字节数等于 len,正常读取并返回 len。 * 实际可用字节数小于 len,但读取已经到达 EOF,或读取进行时被 signal 打断,函数返回实际读取到的字节数。 * 实际可用字节数小于 len,但并非因为中断或 EOF,而是因为可供读取的数据尚未出现(例如等待标准输入)。此时若文件描述符以非 O_NONBLOCK 模式打开时,read 函数阻塞(进程睡眠);以 O_NONBLOCK 模式打开时,函数返回 -1,errno 为 EAGAIN。 * 尚未开始读取就被 signal 打断,函数返回 -1,errno 为 EINTR。 * 发生其他严重错误(例如 I/O 错误),函数返回 -1,errno 为 EAGAIN 和 EINTR 以外的其他值,比如 EBADF(bad file,fd 无效或未打开)、EFAULT(fault,buf 指向的地址空间不属于本进程)、EINVAL(invalid,fd 指向了一个不允许读取的目标)、EIO(低级 I/O 错误)。 读取的字节限制:ssize_t 的最大值是 SSIZE_MAX(LONG_MAX)。 == 写入 == ssize_t write(int fd, const void* buf, size_t count); 是写入函数的函数原型,调用时从 buf 中读取 count 个字节写入 fd 所指向的文件。写入过程和 read 一样会推进文件的“当前”位置指针。 write 函数的写入有以下几种情况: * 写入成功,返回 ssize_t 数据类型,表示写入的字节数。 * 写入失败,返回 -1 并设置 errno。 写入不存在 EOF,所以返回 0 没有特殊的意义,仅仅代表写入了 0 字节。正常来说 write 不会像 read 一样出现部分写入,也就是正常情况下返回的 ssize_t 取值上是等于 count 的。但对于特殊的文件(比如 socket)是有可能出现部分写入的。 写入的方式会受到 fd 是否以 O_APPEND 模式打开的影响。开启附加模式之后,写入动作不会从“当前”位置开始,而是从文件末尾开始。这和手动将“当前”位置指针移动到文件末尾不同,附加模式可以避免多个进程写入同一个文件时候出现的竞争问题,确保写入总是从末端开始。对于日志记录来说,附加模式十分有用。 写入的字节限制也是 SSIZE_MAX。
回应 2012-04-17 21:01:17 -
松鼠亲自奥利奥 (all men are created equal)
== 信号 == 信号(signal)是一种单向一部通知机制。信号可以从内核送往一个进程,从一个进程送往另一个进程,或者从一个进程送给它自己。 在知乎上我也就这个问题提出了问题([http://www.zhihu.com/question/20175801/answer/14225549]),总结出信号具有以下特点: * 信号是一种事件通知机制,但不是单纯的通知,而是伴随着“软中断”的。 * 基于上一点,信号的发送与进程的主事件流是异步的。信号一旦到达,就会抢占进程控...2012-04-16 23:51:42 1人喜欢
== 信号 ==
信号(signal)是一种单向一部通知机制。信号可以从内核送往一个进程,从一个进程送往另一个进程,或者从一个进程送给它自己。 引自 介绍基本概念 在知乎上我也就这个问题提出了问题(http://www.zhihu.com/question/20175801/answer/14225549),总结出信号具有以下特点: * 信号是一种事件通知机制,但不是单纯的通知,而是伴随着“软中断”的。 * 基于上一点,信号的发送与进程的主事件流是异步的。信号一旦到达,就会抢占进程控制权,优先调用 signal_handler 函数,然后才返回主事件流。 一般进程对 Linux 内核实现的三十多种信号都有默认的处理方式(signal_handler),程序编写者可以注册自己的 handler 取代默认处理,除了终止进程信号(SIGKILL)和中止进程信号(SIGSTOP)。 == 进程间通信 == Linux 所支持的进程间通信(IPC)方式还包括管道(pipe)、命名管道(FIFO)、信号量(semaphore)、消息队列、共享内存、快速用户空间 mutex(futex)。 == 错误处理 == C 语言没有内置的异常机制,一般系统调用函数返回 -1 代表出错。错误类型的标识会记录在 <errno.h> 中的全局变量 errno 中。perror 函数可以根据错误标识打印错误信息,strerror 可以接受 errno 为参数输出错误信息。常见的错误是打印错误信息之前调用了其他函数,这些函数执行过程可能也会修改 errno 的值。应该在发生这种情况时提前用一个整型变量保存 errno。
回应 2012-04-16 23:51:42
-
松鼠亲自奥利奥 (all men are created equal)
1. 文件描述符(file descriptor)是对已打开文件的引用,每个打开的文件会被分配一个独一无二的 fd,fd 由用户空间程序所共享。多个进程之间可以共享 fd,内核并不会加以限制。 2. 文件名(filename)可以用于访问文件,但文件的直接引用是通过 inode(信息节点 information node)来进行的。文件名可以看成是映射表中的关键字,一个文件名只会对应一个唯一的 inode,一个 inode 却可以被多个文件名所对应(通过硬链接)。 3. ...2012-04-13 18:15:17
1. 文件描述符(file descriptor)是对已打开文件的引用,每个打开的文件会被分配一个独一无二的 fd,fd 由用户空间程序所共享。多个进程之间可以共享 fd,内核并不会加以限制。 2. 文件名(filename)可以用于访问文件,但文件的直接引用是通过 inode(信息节点 information node)来进行的。文件名可以看成是映射表中的关键字,一个文件名只会对应一个唯一的 inode,一个 inode 却可以被多个文件名所对应(通过硬链接)。 3. 目录是一种特殊的文件。 4. 文件名和 inode 所配成的对称为“链接”(link),硬链接(hard link)直接将文件名指向 inode,所以一个 inode 形成的多个硬链接之间是完全平等的。每个 inode 会携带有链接计数(link count),类似于 GC 中的引用计数器,删除一个文件名 link count 减一,达到零后 inode 才被删除。硬链接基于文件系统,所以无法建立跨文件系统的硬链接。 5. 符号链接(symbolic link、symlink)类似于“快捷方式”(shortcut),是使用一个特殊的文件来引用某个路径(filename)的方式,它拥有自己的 inode 和文件名。因为被引用文件是记录在符号链接自己的 inode 中的,所以甚至可以指向一个不存在的文件,形成 broken link。符号链接在解析时开销比硬链接大,但可以跨文件系统。 6. 文件不一定是文件系统中的物理文件,设备通信、进程间通信、网络都可以以文件的形式抽象,从而形成特殊文件。其中“块设备文件”将设备通信抽象为可以随机访问的文件,例如光驱、硬盘;“字符设备文件”将设备抽象为可以线性顺序访问的文件,例如键盘。除此之外还有命名管道(named pipl、FIFO)和 Unix domain socket 也以文件形式抽象。 7. Linux 的多个文件系统(filesystem)通过挂载(mounting)和卸载(unmounting)机制,可以被放置到统一的全局命名空间下的某个挂载点(mount point)上。其中“/”为根文件系统(root filesystem)。其他形式的文件(虚拟文件系统 virtual filesystem、网络文件系统 network filesystem)也可以以类似原理被挂载。
回应 2012-04-13 18:15:17 -
松鼠亲自奥利奥 (all men are created equal)
进程(process)是执行中的目标码(object code):正在运行的程序。但它不仅仅是目标码,进程由数据、资源、状态以及一个虚拟的计算机组成。 一个进程在内存中比较有意义的有 text、data 和 bbs 三段。text 段包含可执行代码和只读数据(例如常量),RX;data 段中包含已经初始化过的数据(例如已定义初值的变量),RW;bbs 段包含尚未初始化的全局数据。进程通常只通过系统调用来请求和操作资源,其所分配得到的资源会被内核...2012-04-15 20:45:48 2人喜欢
进程(process)是执行中的目标码(object code):正在运行的程序。但它不仅仅是目标码,进程由数据、资源、状态以及一个虚拟的计算机组成。 引自 介绍基本概念 一个进程在内存中比较有意义的有 text、data 和 bbs 三段。text 段包含可执行代码和只读数据(例如常量),RX;data 段中包含已经初始化过的数据(例如已定义初值的变量),RW;bbs 段包含尚未初始化的全局数据。进程通常只通过系统调用来请求和操作资源,其所分配得到的资源会被内核存入进程描述符(process descriptor)。 进程本身是一个虚拟的抽象对象。每个进程由一个或多个线程(threads of execution)构成,而线程则是最小执行单位。传统 Unix 程序惯用每个进程单线程的程序,因为 Unix 简单快速创建进程的机制和稳固的进程间通信(IPC)机制降低了对线程的需求。线程由存放局部变量的堆栈(stack)、处理机状态和目标码执行位置指针组成。进程的其余部分则由进程内的多个线程共享。
Linux 中的授权机制由用户(user)和组(group)提供。每个用户都会被分配一个独一无二的正整数,称为用户标识符(user ID,简称 uid)。每个进程恰好会被关联到一个 uid,用来识别运行进程的用户,这被称为进程的真实用户标识符(real uid)。 引自 介绍基本概念 uid 和 username 的对应存储在 /etc/passwd 中。登录系统时 login 询问用户名和密码,如果有效则衍生(spawn)出 login shell,让 shell 的 real uid 等于登录用户的 uid。子进程的 uid 继承自父进程。uid 的所有权属于 root,只有 root 可以更改进程的 real uid,故 login 必须使用 root 运行。 每个进程除了 real uid 还拥有effective uid(有效用户标识)、saved uid、filesystem uid(文件系统用户标识)。real uid 一般代表启动进程;effective uid 可以更改(以适应权限分配需要),它将决定实际权限;saved uid 保存上一次 effective uid 的值;filesystem uid 决定访问文件系统的权限,一般情况下等于 effective uid。 每个用户可能隶属于一个或多个组(group),包括 /etc/passwd 列出的主要组(primary group)和登录组(login group),以及 /etc/group 中的追加组(supplemental group)。每个进程也会关联到一系列的组标识(group ID,缩写 gid),进程 gid 的种类和进程 uid 的种类对应。进程一般关联到用户的登录组而非追加组。 文件的权限用总共 9 bit 的二进制码表示,其中文件拥有者(owner)权限、拥有者所在组权限、其他用户权限各使用 3 bit ,三个置为为 1 分别代表拥有读取权限(R)、拥有写入权限(W)、拥有执行权限(X)。权限码的每三位可以用十进制表示,例如 777 就表示 owner、owner group、other 都拥有 R、W、X 权限。
回应 2012-04-15 20:45:48 -
松鼠亲自奥利奥 (all men are created equal)
== 信号 == 信号(signal)是一种单向一部通知机制。信号可以从内核送往一个进程,从一个进程送往另一个进程,或者从一个进程送给它自己。 在知乎上我也就这个问题提出了问题([http://www.zhihu.com/question/20175801/answer/14225549]),总结出信号具有以下特点: * 信号是一种事件通知机制,但不是单纯的通知,而是伴随着“软中断”的。 * 基于上一点,信号的发送与进程的主事件流是异步的。信号一旦到达,就会抢占进程控...2012-04-16 23:51:42 1人喜欢
== 信号 ==
信号(signal)是一种单向一部通知机制。信号可以从内核送往一个进程,从一个进程送往另一个进程,或者从一个进程送给它自己。 引自 介绍基本概念 在知乎上我也就这个问题提出了问题(http://www.zhihu.com/question/20175801/answer/14225549),总结出信号具有以下特点: * 信号是一种事件通知机制,但不是单纯的通知,而是伴随着“软中断”的。 * 基于上一点,信号的发送与进程的主事件流是异步的。信号一旦到达,就会抢占进程控制权,优先调用 signal_handler 函数,然后才返回主事件流。 一般进程对 Linux 内核实现的三十多种信号都有默认的处理方式(signal_handler),程序编写者可以注册自己的 handler 取代默认处理,除了终止进程信号(SIGKILL)和中止进程信号(SIGSTOP)。 == 进程间通信 == Linux 所支持的进程间通信(IPC)方式还包括管道(pipe)、命名管道(FIFO)、信号量(semaphore)、消息队列、共享内存、快速用户空间 mutex(futex)。 == 错误处理 == C 语言没有内置的异常机制,一般系统调用函数返回 -1 代表出错。错误类型的标识会记录在 <errno.h> 中的全局变量 errno 中。perror 函数可以根据错误标识打印错误信息,strerror 可以接受 errno 为参数输出错误信息。常见的错误是打印错误信息之前调用了其他函数,这些函数执行过程可能也会修改 errno 的值。应该在发生这种情况时提前用一个整型变量保存 errno。
回应 2012-04-16 23:51:42
-
松鼠亲自奥利奥 (all men are created equal)
## 进程组 * 每个进程都属于一个进程组(process group) * 可以把信号(signal)传递给整个进程组 * 每个进程组都有一个首领进程(process group leader),它的 pid 就是进程组的 pgid * 只要还有一个进程存活,即使首领进程已终止,进程组仍然存在 ## 会话 * 登录一台机器时,登录进程(login process)会为其创建一个会话(session),它只包含一个进程 login shell 也即会话首领进程(session leader),其 pid 作为 sessi...2013-01-01 10:37:23
## 进程组 * 每个进程都属于一个进程组(process group) * 可以把信号(signal)传递给整个进程组 * 每个进程组都有一个首领进程(process group leader),它的 pid 就是进程组的 pgid * 只要还有一个进程存活,即使首领进程已终止,进程组仍然存在 ## 会话 * 登录一台机器时,登录进程(login process)会为其创建一个会话(session),它只包含一个进程 login shell 也即会话首领进程(session leader),其 pid 作为 session ID * 一个会话至少包含一个前台进程组(foreground process group),可包含 0~n 个后台进程组(background process group) * 前台进程组可被信号集体杀死 - ^D : SIGQUIT - 网络断开 : SIGHUP - ^C : SIGINT * 通过管道连接的进程属于同一个进程组,如果没有用结尾加 & 之类的方式,该进程组在终端中运行时为前台进程组
// related: unistd.h pid_t setsid(void); // 创建新会话, 会话中包含一个进程组, 首领为当前进程 int setpgid(pid_t pid, pid_t pgid); // 更改 pid 进程的进程组为 pgid,不可改现任首领进程,pid 必须属于当前进程树
## 守护进程 * 登录会话一般会关联到 tty 或其他终端设备,但守护进程(daemon)有自己的会话,不和 tty 关联,创建自己的会话是为了避免被其他会话的集体杀牵连,setsid 做到这点 * daemon 必须是 init 的子进程,可以通过 fork 后父进程自杀,子进程被托孤来做到这点 * daemon 的工作目录应该是根目录,以防工作目录被 unmount,chdir 做到这点 * daemon 的 stdin, stdout, stderr 都被重定向到 /dev/null * 已打开 fd 要关掉 一步做到:
int daemon(int nochdir, int noclose); // unistd.h
特殊情况:父进程 fork 之后还没来得及自杀子进程就终止了,这种情况下子进程会成为僵尸,对于 daemon 来说一般通过两次 fork 来避免这种情况,即杀死僵尸进程的父进程(第一次 fork 的产物),这时第二次 fork 产生的进程如果成为僵尸,会因为被托孤给 init 而被 init 清理。
回应 2013-01-01 10:37:23 -
松鼠亲自奥利奥 (all men are created equal)
文件 I/O 在 Unix 和 Linux 中都具有重要意义,因为操作系统对所有具有输入输出的对象都以“一切都是文件”的方式来抽象。包括设备文件、管道、目录、futex、FIFO、socket 都可以被抽象为文件来供操作。 一个文件必须先打开,才可以对它进行操作。内核会替每个进程维护一份已打开文件列表,称为文件表(file table)。此表是通过非负整数被索引,而此非负整数称为文件描述符(file descriptor,缩写 fd)。 这一段文字有点费解...2012-04-17 21:01:17 1人喜欢
文件 I/O 在 Unix 和 Linux 中都具有重要意义,因为操作系统对所有具有输入输出的对象都以“一切都是文件”的方式来抽象。包括设备文件、管道、目录、futex、FIFO、socket 都可以被抽象为文件来供操作。
一个文件必须先打开,才可以对它进行操作。内核会替每个进程维护一份已打开文件列表,称为文件表(file table)。此表是通过非负整数被索引,而此非负整数称为文件描述符(file descriptor,缩写 fd)。 引自 文件 I/O 这一段文字有点费解,我查找了其他资料辅助,得到的说法是:文件描述符并非进程内唯一,而是全局唯一存在。对于一个已启动的操作系统,它是跨进程存在的。而 file table 在 Linux Tutorial Glossary 中的描述是:“A table internal to the kernel that maintains the the relationship between the file descriptors and the physical file on the hard disk.” 可见其也是“internal to the kernel”的,是维护文件描述符(fd)和物理文件(可以看成 inode)关系的一张表。
默认情况下,子进程会取得其父进程的文件表。 引自 文件 I/O 在 man fork 中看到了关于文件描述符被子进程继承的说法:“The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two descriptors share open file status flags, current file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).” 可见子进程对一个已打开的文件的属性信息还是会继承的。 综合书中说法,file table 应该指的就是关于已打开文件属性的记录结构:
该表(file table)中每个条目内含与已打开文件相关的信息,包括一个指针(指向文件 inode 在内存中的副本)以及相关的元数据(例如文件位置与访问模式)。 引自 文件 I/O 一个文件描述符可表示成一个整型数据,默认情况下上限为 1024,其中有特殊的三个:0-标准输入-stdin,1-标准输出-stdout,2-标准错误-stderr。 == 打开文件 == 文件控制系统调用的 C API 包含在 fcntl.h 头文件中。 * 打开文件:int open(const char *name, int flags[, mode_t mode]); * flags 至少需要包含 O_RDONLY(read only)、O_WRONLY(write only)或 O_RDWR(read and write)之一,其他参数可以通过与操作符(|)附加。 * O_APPEND 附加模式(位置指针自动指向末尾)。 * O_ASYNC 异步模式,如果文件可读取(可写入)了则产生一个 SIGIO 信号。只能用于 sockets 不能用于常规文件。 * O_CREAT(create 怎么少了一个字母呢)如果文件不存在则自动创建。 * O_DIRECT 直接 I/O,让内核尽量减少 I/O 管理在场机会,跳过内核缓存、缓冲。启用后文件偏移量必须是底层设备的扇区大小整数倍。 * O_DIRECTORY 若不是目录则调用失败。一般不直接使用,而是供 opendir 链接库调用。 * O_EXCL 和 O_CREAT 相反,如果文件已经存在则调用失败。用于避免创建文件时竞争条件(race condition)。 * O_LARGEFILE 以 64 位偏移量打开文件,以便支持打开大于 2G 的文件。64 位架构上是默认值。 * O_NOFOLLOW 不跟踪 symlink 重定向——如果文件是符号链接则调用失败。 * O_NONBLOCK 非阻塞模式,让进程在 I/O 时不会被阻塞(休眠),仅用于 FIFO。 * O_SYNC 同步模式,阻塞 I/O 函数的调用。一般读取默认是同步模式,而写入则采取内核缓冲和 writeback 机制,开启该模式则关闭内核缓冲。 * O_TRUNC 把文件截断成零长度,只对允许写入的常规文件有效,对终端和 FIFO 会被忽略。 == 权限问题 == 默认文件拥有者(owner)就是创建文件进程的 effective uid。 默认文件拥有组(owner group)在 System V(大部分 Linux 的行为模型)中是设定为创建文件进程的 effective gid,在 BSD 中是设定为上层目录的 gid。 在用 open 打开文件的时候如果指定了 O_CREAT 则必须使用 mode 参数指定访问模式。mode 访问模式可以和打开模式抵触,因为打开文件的时候并不会检查 mode。mode 参数就是 Unix 权限位,可以设置为 S_IRWXU(111xxxxxx,owner 可以读写执行)、S_IRUSR(100xxxxxx,owner 可读)、S_IWUSR(010xxxxxx,owner 可写)、S_IXUSR(001xxxxxx,owner 可执行)、S_IRWXG(xxx111xxx,group 可读写执行)、S_IRGRP(xxx100xxx,group 可读)、S_IWGRP(xxx010xxx,group 可写)、S_IXGRP(xxx001xxx,group 可执行)、S_IRWXO(xxxxxx111,other 可读写执行)、S_IROTH(xxxxxx100,other 可读)、S_IWOTH(xxxxxx010,other 可写)、S_IXOTH(xxxxxx001, other 可执行)。权限设置可以使用与操作符来连接。 我觉得去记这么一堆东西实在是很傻的事情,我更愿意直接书写权限码,例如 S_IRWXU | S_IRWXG | S_IXOTH 直接写成 771。 文件操作的实际权限并非直接使用 mode 参数,而是将 mode 与 umask 的补码进行一次与运算,也就是从 umask 中的设置位“拿掉” mode 中的权限。 == 快捷创建新文件、错误处理 == creat(const char* name, mode_t mode) 是 O_WRONLY | O_CREAT | O_TRUNC 组合的快捷方式。 按照惯例,open 和 creat 在失败的时候不返回文件描述符,而是返回 -1,同时 errno 会被设置。 == 读取 == ssize_t read(int fd, void* buf, size_t len); 是读取函数的函数原型,调用时会从“当前”位置开始向后读取 len 个字节,并存入 buf 所指向的地址。读取过程“当前”位置会随之前进,就像迭代器指针一样。 read 函数的读取有以下几种情况: * 实际可用字节数为 0(已到达文件末尾),函数返回 0 表示 EOF。 * 实际可用字节数等于 len,正常读取并返回 len。 * 实际可用字节数小于 len,但读取已经到达 EOF,或读取进行时被 signal 打断,函数返回实际读取到的字节数。 * 实际可用字节数小于 len,但并非因为中断或 EOF,而是因为可供读取的数据尚未出现(例如等待标准输入)。此时若文件描述符以非 O_NONBLOCK 模式打开时,read 函数阻塞(进程睡眠);以 O_NONBLOCK 模式打开时,函数返回 -1,errno 为 EAGAIN。 * 尚未开始读取就被 signal 打断,函数返回 -1,errno 为 EINTR。 * 发生其他严重错误(例如 I/O 错误),函数返回 -1,errno 为 EAGAIN 和 EINTR 以外的其他值,比如 EBADF(bad file,fd 无效或未打开)、EFAULT(fault,buf 指向的地址空间不属于本进程)、EINVAL(invalid,fd 指向了一个不允许读取的目标)、EIO(低级 I/O 错误)。 读取的字节限制:ssize_t 的最大值是 SSIZE_MAX(LONG_MAX)。 == 写入 == ssize_t write(int fd, const void* buf, size_t count); 是写入函数的函数原型,调用时从 buf 中读取 count 个字节写入 fd 所指向的文件。写入过程和 read 一样会推进文件的“当前”位置指针。 write 函数的写入有以下几种情况: * 写入成功,返回 ssize_t 数据类型,表示写入的字节数。 * 写入失败,返回 -1 并设置 errno。 写入不存在 EOF,所以返回 0 没有特殊的意义,仅仅代表写入了 0 字节。正常来说 write 不会像 read 一样出现部分写入,也就是正常情况下返回的 ssize_t 取值上是等于 count 的。但对于特殊的文件(比如 socket)是有可能出现部分写入的。 写入的方式会受到 fd 是否以 O_APPEND 模式打开的影响。开启附加模式之后,写入动作不会从“当前”位置开始,而是从文件末尾开始。这和手动将“当前”位置指针移动到文件末尾不同,附加模式可以避免多个进程写入同一个文件时候出现的竞争问题,确保写入总是从末端开始。对于日志记录来说,附加模式十分有用。 写入的字节限制也是 SSIZE_MAX。
回应 2012-04-17 21:01:17
论坛 · · · · · ·
跟APUE比起来 这本书 小而精 | 来自北风之神 | 2016-02-05 15:28:01 |
这本书的其他版本 · · · · · · ( 全部6 )
-
O'Reilly Media (2007)8.4分 70人读过
-
O'Reilly Media (2013)9.0分 44人读过
-
东南大学出版社 (2014)8.9分 44人读过
-
人民邮电出版社 (2014)8.3分 32人读过
-
限时抢
在哪儿借这本书 · · · · · ·
以下书单推荐 · · · · · · ( 全部 )
谁读这本书?
二手市场
订阅关于LINUX系统编程的评论:
feed: rss 2.0
0 有用 Tony Bai 2011-11-22 15:04:11
如果搞linux系统编程,这算是一入门+简要手册类的书吧。值得快速浏览一遍。
0 有用 y 2011-02-10 20:30:19
看了两遍. 不错. 比APUE简单.
1 有用 a 2013-01-22 05:18:56
翻译的比较无语, 很大篇幅就是函数接口说明
0 有用 absolute 2012-03-04 20:29:44
基本的接口调用方法的描述
0 有用 Tsing 2012-02-24 20:38:20
Stevens的书可替之
0 有用 最难不过二叉树 2018-04-02 08:30:27
感觉很一般啊
0 有用 Lasciare 2017-12-10 17:09:25
短小精悍是这本书最大的特点了吧。。毕竟读apue还是太厚了,但是不得不说很多讲解没有apue讲的细腻
0 有用 阅微草堂 2017-04-08 19:38:27
select()poll()事件驱动模型的关键函数。库函数和系统调用的关系 :一系列动作。Linux操作系统的诞生、发展和成长过程依赖于以下五个重要支柱:UNIX操作系统、MINIX操作系统、GNU计划、POSIX标准和Internet。
0 有用 Heap 2016-08-10 22:19:54
穿插讲解2.6内核的一些原理,对操作系统可以有更深入的认识
1 有用 maxy218 2016-07-01 07:42:08
这个版本的翻译不行。很简单的句子都翻译不通,很容易误导人