<Unix环境高级编程>--信号
这篇是关于在读<Unix环境高级编程>信号一章时做的一些笔记,特记录在此。
关于多线程,多进程这几章的笔记,写得太乱,正在尝试更正。
以下开始正文
-----------------------------------------------------------------------------------------
1.信号和pause()函数
调用pause()函数可以让进程的状态变为休眠,休眠到什么时候呢?答案是:当进程中一
个信号产生时。
当进程调用pause()函数休眠的时候,信号来临,此时,内核将自动将进程唤醒。
以下的代码可以使用pause让进程中休眠一段时间后,继续执行。
signal(SIG_ALRM,slg_alrm);
alarm(3);
while(need_to_do==0)
pause();
void slg_alrm()
{
need_to_do=1;
}
第一步,进程调用alarm函数设定一个超时器,3秒后SIG_ALRM进程将会准时到达。
第二步,进程在pause()程序语句中转入休眠状态。
第三步,SIG_ALRM到达后,sig_alrm被处理,将变量need_to_do设置为1。
第四步,内核将此进程唤醒,此进程将从休眠状态转为可调度状态。此进程被分配到CPU后,进程将从pause()指令的下一条指令开始执行。
第五步,此时need_to_do已经变更为1,所以,pause函数将不会被执行了。这样,就模
拟了一个让进程休眠三秒的动作。
2.中断系统调用和自动重启动
在Linux系统中,总有某些函数是属于低速系统调用,当调用低速系统调用函数时,进程会被阻塞,哪些是低速系统调用呢?
比如,当需要进行I/O函数操作时(read/write函数),因为需要访问I/O设备,其肯定属于低速系统调用,在访问该I/O设备时,进程可能会被阻塞一段时间,又或者,当进程需要>从键盘读入字符(read(STDIN))时,进程肯定会被阻塞,直到用户在键盘中输入完毕,函数才能返回。
那么,当以上低速系统调用期间,如果进程产生信号,怎么处理?
如:
if((n=read(STDIN,buf,BUFSIZE))<0)
{
//...
}
以上代码,如果进程在执行read指令期间发生中断(信号产生),处理完中断后,进程该从哪儿开始执行呢?重新执行read吗?还是read的下一条指令?还是产生异常退出?
在早期的UNIX系统中,有一个特性就是:如果进程在执行一个低速系统调用而阻塞间捕捉到一个信号,则该系统调用就被中断不再继续执行,系统调用返回出错,其error值被置为EINTR. 早期对于低速系统调用的处理大多都是这样的:
agin:
if((n=read(STDIN,buf,BUFSIZE))<0) //对于中断,系统将会返回出错
{
if(errno==EINTR)
goto agin; //重新执行read
else
//其它错误。
}
else
//Success
为了帮助应用程序不必处理被中断的系统调用,就引入了自动重启动机制。自动重启动的函数包括:ioctl,read,readv,write,writev,wait,waitpid.
对于以上函数,在执行期间,如果被信号中断,则该函数可以再次被调用。
以上,wait,waitpid并不是针对低速设备调用阻塞而使用的函数,把它们加入到自动重启动是因为:这两个函数碰到信号总是会被中断!!
对于以上函数默认的自动重启动,有时会产生一些问题,所以,现在允许进程基于每个信号禁用自动重启动的功能。
3.可重入函数
在讨论可重和函数之前,先来看一下以下的代码:
int gobal_var = 0;
signal(SIG_TERM,sig_term);
void sig_term
{
gobal_var = 0;
//do other
}
void func()
{
gobal_var = 1; //设置全局变量为1
if(gobal_var)
// do other code.
}
以上,func函数设置全局变为1,继续下面的指令,如果在if(gobal_var)这条指令执行前,发生了SIG_TERM中断,则全局变为gobal_var被置为0,返回后,if语句下面的指令将不会被执行。
以上有一个特征,就是在发生中断时,进程的全局数据空间导致了变更,调用func函数时,如果有中断或没有中断产生,函数产生的结果可能不一样。
另外一个例子,假设gobal_var是在func中保存了进程的pid号,我们将func作为一个函数库发布,在多进程环境中,每个进程调用func产生的结果也可能不一样。
在<unix环境高级编程>中也有一个例子,就是进程正在执行malloc,此时捕捉到信号,执行信号处理函数,其中又会调用malloc函数,因为对于进程来说,malloc函数为进程所分配的存储区维护一张链表,执行信号处理程序时,进程可能正在更改此链表,如果继续执行一次malloc,则此链表可能遭受破坏。
现在我们引入可重入函数:什么是可重入函数?
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误,也可以理解为,每个任务得到的数据都是相同的。另外一个特征:可重入函数可以在任意时刻被中断,稍后继续运行,不会丢失数据。
所以,可重入函数内部大部分都会使用本地变量,或者,如果要使用全局变量进,会保护自己的数据。
当一个函数中使用以下几种实现时,他们很可能就不是可重入的函数:
(a): 使用静态的数据结构或者全局变量。
(b): 调用malloc或者free函数.
(c): 调用了其它不可重函数
(d): 调用了I/O库。
总体来说的话,一个函数在重入条件下(可以看成再次调用,或者从中断返回)条件下使用了未受保护的共享资源,则它是不可重入的.
关于多线程,多进程这几章的笔记,写得太乱,正在尝试更正。
以下开始正文
-----------------------------------------------------------------------------------------
1.信号和pause()函数
调用pause()函数可以让进程的状态变为休眠,休眠到什么时候呢?答案是:当进程中一
个信号产生时。
当进程调用pause()函数休眠的时候,信号来临,此时,内核将自动将进程唤醒。
以下的代码可以使用pause让进程中休眠一段时间后,继续执行。
signal(SIG_ALRM,slg_alrm);
alarm(3);
while(need_to_do==0)
pause();
void slg_alrm()
{
need_to_do=1;
}
第一步,进程调用alarm函数设定一个超时器,3秒后SIG_ALRM进程将会准时到达。
第二步,进程在pause()程序语句中转入休眠状态。
第三步,SIG_ALRM到达后,sig_alrm被处理,将变量need_to_do设置为1。
第四步,内核将此进程唤醒,此进程将从休眠状态转为可调度状态。此进程被分配到CPU后,进程将从pause()指令的下一条指令开始执行。
第五步,此时need_to_do已经变更为1,所以,pause函数将不会被执行了。这样,就模
拟了一个让进程休眠三秒的动作。
2.中断系统调用和自动重启动
在Linux系统中,总有某些函数是属于低速系统调用,当调用低速系统调用函数时,进程会被阻塞,哪些是低速系统调用呢?
比如,当需要进行I/O函数操作时(read/write函数),因为需要访问I/O设备,其肯定属于低速系统调用,在访问该I/O设备时,进程可能会被阻塞一段时间,又或者,当进程需要>从键盘读入字符(read(STDIN))时,进程肯定会被阻塞,直到用户在键盘中输入完毕,函数才能返回。
那么,当以上低速系统调用期间,如果进程产生信号,怎么处理?
如:
if((n=read(STDIN,buf,BUFSIZE))<0)
{
//...
}
以上代码,如果进程在执行read指令期间发生中断(信号产生),处理完中断后,进程该从哪儿开始执行呢?重新执行read吗?还是read的下一条指令?还是产生异常退出?
在早期的UNIX系统中,有一个特性就是:如果进程在执行一个低速系统调用而阻塞间捕捉到一个信号,则该系统调用就被中断不再继续执行,系统调用返回出错,其error值被置为EINTR. 早期对于低速系统调用的处理大多都是这样的:
agin:
if((n=read(STDIN,buf,BUFSIZE))<0) //对于中断,系统将会返回出错
{
if(errno==EINTR)
goto agin; //重新执行read
else
//其它错误。
}
else
//Success
为了帮助应用程序不必处理被中断的系统调用,就引入了自动重启动机制。自动重启动的函数包括:ioctl,read,readv,write,writev,wait,waitpid.
对于以上函数,在执行期间,如果被信号中断,则该函数可以再次被调用。
以上,wait,waitpid并不是针对低速设备调用阻塞而使用的函数,把它们加入到自动重启动是因为:这两个函数碰到信号总是会被中断!!
对于以上函数默认的自动重启动,有时会产生一些问题,所以,现在允许进程基于每个信号禁用自动重启动的功能。
3.可重入函数
在讨论可重和函数之前,先来看一下以下的代码:
int gobal_var = 0;
signal(SIG_TERM,sig_term);
void sig_term
{
gobal_var = 0;
//do other
}
void func()
{
gobal_var = 1; //设置全局变量为1
if(gobal_var)
// do other code.
}
以上,func函数设置全局变为1,继续下面的指令,如果在if(gobal_var)这条指令执行前,发生了SIG_TERM中断,则全局变为gobal_var被置为0,返回后,if语句下面的指令将不会被执行。
以上有一个特征,就是在发生中断时,进程的全局数据空间导致了变更,调用func函数时,如果有中断或没有中断产生,函数产生的结果可能不一样。
另外一个例子,假设gobal_var是在func中保存了进程的pid号,我们将func作为一个函数库发布,在多进程环境中,每个进程调用func产生的结果也可能不一样。
在<unix环境高级编程>中也有一个例子,就是进程正在执行malloc,此时捕捉到信号,执行信号处理函数,其中又会调用malloc函数,因为对于进程来说,malloc函数为进程所分配的存储区维护一张链表,执行信号处理程序时,进程可能正在更改此链表,如果继续执行一次malloc,则此链表可能遭受破坏。
现在我们引入可重入函数:什么是可重入函数?
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误,也可以理解为,每个任务得到的数据都是相同的。另外一个特征:可重入函数可以在任意时刻被中断,稍后继续运行,不会丢失数据。
所以,可重入函数内部大部分都会使用本地变量,或者,如果要使用全局变量进,会保护自己的数据。
当一个函数中使用以下几种实现时,他们很可能就不是可重入的函数:
(a): 使用静态的数据结构或者全局变量。
(b): 调用malloc或者free函数.
(c): 调用了其它不可重函数
(d): 调用了I/O库。
总体来说的话,一个函数在重入条件下(可以看成再次调用,或者从中断返回)条件下使用了未受保护的共享资源,则它是不可重入的.