Unix高级环境编程系列笔记
终于可以安静的坐下来仔细的阅读这本厚厚的书了,它就是“ Advanced Programming in the UNIX Environment”,中文名叫《Unix高级环境编程》,简称APUE。这本书被誉为Unix程序员的圣经,所以它有多经典不用我再赘述了。我读的是英文第二版,感觉挺费劲儿。说说感受,首先,读这本书需要有一些Unix的编程经验,书中论述的很多问题并不容易明白。对于一些接口的特性,如果没有实际经验很难理解。书中还涉及一些编程中会遇到的陷阱,这些陷阱也常常是实际编程中容易被人忽略的。其次,读这本书感觉有些枯燥,作者通过逐一的介绍Unix系统的API来深入全面的说明Unix系统的特性以及系统编程所涉及到的方方面面,大量的细节描述也容易产生疲倦。第三,这的确是本关于Unix的百科全书,大量精确的描述还是让我不得不仰视下大师的风采。第四,我的英文水平实在有待于提高,不但读的慢,而且还会对于一些概念理解上出现较大偏差。第五,这本书读起来确实很辛苦。
为了不让我的辛苦白费,为了更加深入准确的理解本书,我决定从今天起开始敲读书笔记。为了不让我的读书比较沦落为对原书的纯粹copy或翻译,我将对于每一个我认为值得记录的知识点列出相应的问题,这样我的读书笔记读起来就更像是个习题集。这样有几个好处,一来可以更加明确某段论述的用途,二来可以加深对于知识的理解,第三带着问题去看会减少枯燥~~对于一些问题,随着我经验的增加,肯定会有新的理解,对于一些错误也要及时纠正,因此我回坚持更新每一篇已经发布的笔记。好了,不说闲话了,这就开始!
1.what is signal?
Signals are a technique used to notify a process that some condition has occurred. three choice for dealing with:
o ignore the signal. (not recommended). SIGKILL和SIGSTOP例外,这两个信号无法被忽略的原因是,要提供给内核或超级用户以确定的方式来杀掉或者停止任何进程。
o let the default action occur.(the default action for most signals is to terminate the process.)
o provide a function that is called when the signal occurs(catching the signal)
2.产生信号量的情况有哪些?
o 当用户按下终止键。如按下Control-C会产生SIGINT信号。
o 硬件异常产生信号。如除0,无效内存引用等。如SIGSEGV在进程执行对于无效内存进行引用时产生。
o kill(2)函数允许一个进程发送任何信号给其它进程或进程组。自然,这会有些限制:我们必须是要发送信号进程的占有者,或者我们拥有超级权限。
o kill(1)发送信号量给其它的进程。这个程序仅仅是kill函数的一个接口。这个命令通常被用来终止后台运行的程序。
o 软件环境可以产生信号量。如:SIGURG当外带数据到达网络连接,SIGPIPE当一个进程向一个读已经终止的管道写数据时,SIGALRM进程所设置的闹钟超时。
3.常用信号量:
o SIGABRT 调用abort函数是产生此信号。进程异常终止。
o SIGALRM alarm函数设置的计时器超时。
o SIGBUS 硬件故障。如某些类型的内存故障
o SIGCHLD 一个进程终止时,将该信号发送给父进程。信号捕捉函数中通过调用wait取得子进程ID和其终止状态。
o SIGCONT 此信号被发送给需要继续运行,但当前处于停止状态的进程。
o SIGFPE 算数运算异常。除0,浮点溢出等。
o SIGHUP 终端接口检测到一个连接断开,将此信号发送给其控制进程。如果会话首进程终止,发送给前台进程组中的每一个进程。通常用此信号通知守护进程实现配置文件的重载等,因为守护进程无控制终端,一般不会接收到此信号。
o SIGINT 用户中断键(Ctrl+C)
o SIGIO 指示异步IO事件。
o SIGKILL 不能被捕捉的或忽略,可杀死任一进程。
o SIGPIPE 在到管道时读进程已终止.
o SIGPOLL 一个可轮询的设备上发送一特定事件产生此信号。
o SIGQUIT 用户按退出键时产生(ctrl+\\),终止前台进程组,同时产生core文件。SIGSTOP 作业控制,用于停止一个进程。
o SIGTERM kill发送的系统默认终止信号
o SIGTTOU后台进程组中的进程试图向控制终端写时产生此信号。
4.函数signal
#includevoid (*signal(int signo, void (*func)(int)))(int); Returns: previous disposition of signal (see following) if OK, SIG_ERR on error
因为signal的语义与实现相关,所以最好使用sigaction函数代替signal函数。
该函数提供对于某一具体信号量的处理方式。
signo参数为信号名。
func的值可以是:
o 常量SIG_IGN,我们告诉系统忽略这个信号。
o 常量SIG_DFL,指定为该信号的默认处理动作
o 当信号发生时将要调用函数的地址
signal函数的返回值为一个函数指针,该函数以一个整型为参数。
signal的函数原型过于复杂,下面提供一种简化形式:
typedef void Sigfunc(int); Sigfunc *signal(int, Sigfunc *);
如果查看系统头文件
可以发现如下定义:
#define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 #define SIG_IGN (void (*)())1
这些常量可以代替“指向函数的指针,该函数需要一个整型参数,而且无返回值”。signal参数以及返回值就可以用这些常量来表示。
注意:对于signal来说,不改变信号处理方式就不能确定信号的当前处理方式。
5.信号处理函数在通过fork产生子进程中是否有效?
当一个进程调用fork时,子进程继承父进程的信号处理方式。因为子进程复制了父进程的存储映像,所以信号处理函数的地址在子进程中是有意义的。
6.
早期版本中,每次接到信号对其进行处理时,随即将该信号动作复位为默认值。这样在一些早期的程序设计书籍中可能出现如下缺陷代码:
int sig_int(); /* my signal handling function */ ... signal(SIGINT, sig_int); /* establish handler */ ... sig_int() { signal(SIGINT, sig_int); /* reestablish handler for next time */ ... /* process the signal ... */ }
这段代码的缺陷在于signal发生后,且在下一次signal函数被调用之前的窗口期,这段时间内如果发生另一次中断信号,则可能导致默认行为的发生。
7.
早期版本的另外一个问题是,在进程不希望某种信号发生时,他不能关闭该信号。进程能做的一切是忽略该信号。
如果要实现,当我们阻止了下列信号的发生,但是如果他们发生了要记录下来,就会有如下有缺陷的处理方法:
int sig_int_flag; /* set nonzero when signal occurs */ main() { int sig_int(); /* my signal handling function */ ... signal(SIGINT, sig_int); /* establish handler */ ... while (sig_int_flag == 0) pause(); /* go to sleep, waiting for signal */ ... } sig_int() { signal(SIGINT, sig_int); /* reestablish handler for next time */ sig_int_flag = 1; /* set flag for main loop to examine */ }
这段代码的工作原理是在进程捕捉到信号之前,进程将调用pause函数进入休眠状态。当捕捉到信号之后,信号量处理程序将标记sig_int_flag设置为非0值。进程在信号量处理函数返回后将自动将进程唤醒,然后继续进行处理。但是该进程可能会出现问题,如果信号在sig_int_flag判断后与pause调用前出现(且仅出现一次),则该函数可能永远陷入休眠状态。
8.可重入函数(Reentrant Functions)
当一个信号量被捕捉并执行相应的处理程序后,进程将继续执行。我们无法确定此时进程执行到何处,当进程执行到malloc,而在信号处理函数中同样存在malloc则有可能发生错误。则malloc就是非可重入函数。
大多数的非可重用函数有如下特征:
o 使用静态数据结构
o 调用malloc或free
o 标准IO库的一部分
应当了解,即使我们在信号处理函数中调用了可重入函数,由于在每一个线程中仅有一个errno,而main函数可能设置了这个值。因此,需要在调用信号处理函数之前保存errno,在信号处理函数之后恢复errno值。
9. 术语说明:generate signal, deliver signal, pending, block.
a signal is generated for a process (or sent to a process) when the event that causes the signal occurs.
a signal is delivered to a process when the action for a signal is taken.
During the time between the generation of a signal and its delivery, the signal is said to pending.
A process has the option of blocking the delivery of a signal. If a signal that is blocked is generated for a process, and if the action for that signal is either the default action or to catch the signal, then the signal remains pending for the process until the process either (a) unblocks the signal or (b) changes the action to ignore the signal.
The system determines what to do with a blocked signal when the signal is delivered, not when it’s generated. This allows the process to change the action for the signal before it’s delivered.
10.什么是信号屏蔽字(signal mask)?
规定了当前要阻塞递送到该进程的信号集。对于每种可能的信号,该屏蔽字中都有一位与之对应。对于某种信号,如果其对应位以设置,则它当前是被阻塞的。进程可以调用sigprocmask来检测和更改其当前信号屏蔽字。
11.如何给一个进程或进程组发送信号?
#includeint kill(pid_t pid, int signo); int raise(int signo); Both return: 0 if OK, -1 on error
kill函数发送一个信号量给一个进程或一组进程。raise函数允许一个进程发送一个信号量给它自己。
对于kill,对应不同的pid有如下4种不同的情况:
o pid > 0 信号量发送给进程号为pid的进程
o pid == 0 The signal is sent to all processes whose process group ID equals the process group ID of the sender and for which the sender has permission to send the signal. Note that the term all processes excludes an implementation-defined set of system processes. For most UNIX systems, this set of system processes includes the kernel processes and init (pid 1).
o pid < 0 The signal is sent to all processes whose process group ID equals the absolute value of pid and for which the sender has permission to send the signal. Again, the set of all processes excludes certain system processes, as described earlier.
o pid == -1 The signal is sent to all processes on the system for which the sender has permission to send the signal. As before, the set of processes excludes certain system processes.
一个进程发送信号量给另外一个进程需要权限。超级用户有权发送信号给任意一个进程,而对于其它的进程,基本的规则是:real or effective ID of the sender has to equal the real or effective user ID of the receiver.
13.如何检测一个进程是否存在?
POSIX.1 定义了0作为null信号。若kill函数的signo参数为0,kill将执行普通的差错检测,但将不会发送任何信号。这通常被用于检测一个进程是否存在。如果我们向一个不存在的进程发送null信号,kill将返回-1,并将errno设置为ESRCH。注意,unix系统会在一定时间后回收进程ID,所以具有特定进程ID的进程不一定是你认为的那个进程。
进程存在性测试并不是原子操作。当kill返回时,被测试的进程可能已经存在了,所以该测试并无多大价值。
建议继续学习:
- 高性能web服务器-读书笔记 (阅读:6851)
- Unix高级环境编程系列笔记 (阅读:4504)
- MySQL 应用小笔记 (阅读:3458)
- JavaScript中级笔记 (阅读:3431)
- 《精通CSS+DIV》学习笔记 (阅读:3103)
- 读书笔记-交互设计精髓[1] (阅读:2827)
- 《高性能网站建设指南》笔记 (阅读:2581)
- 读《Web 表单设计》 (阅读:2273)
- 《瞬间之美》读书笔记 (阅读:2058)
- 读书:《SEO实战密码》 (阅读:1870)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:nebula 来源: Nebula's Cyberspace
- 标签: Unix高级环境编程 笔记
- 发布时间:2011-02-22 23:28:27
- [46] 界面设计速成
- [43] Oracle MTS模式下 进程地址与会话信
- [42] IOS安全–浅谈关于IOS加固的几种方法
- [42] 视觉调整-设计师 vs. 逻辑
- [41] android 开发入门
- [40] 图书馆的世界纪录
- [39] 【社会化设计】自我(self)部分――欢迎区
- [39] 如何拿下简短的域名
- [37] 程序员技术练级攻略
- [35] 读书笔记-壹百度:百度十年千倍的29条法则