LTZ做作业之APUE8

LTZ做作业之APUE8

信号
提供异步时间处理方式
触发时机:
1.终端命令
2.硬件异常,由kernel抛向对应的Process
3.kill函数/kill命令(超级用户or Process的user相同,这里的user id一般是指实际用户ID or 有效用户ID,如果支持 _POSIX_SAVED_IDS,那么检查saved-user-id)
4.软件触发(满足信号条件)

这里也稍微解释下Kill这个东西,kill函数,kill命令并不是字面上杀掉某些东西,kill只是在特定的时间发送信号,
具体的处理取决于信号本身和信号的处理方式。

信号的处理方式:
这个有点类似Java Exception的处理方式,catch住or往上throw

APUE讲的信号处理方式有三种
1.忽略,SIGKILL/SIGSTOP不能忽略;一般如果Process自己不管的话,应该会走到系统默认的处理流程中,所以不能忽略这件事情是系统自己就会保证的。
2.捕捉信号,例如SIGCHLD,一般parent会call waitpid取子进程终止状态,避免他处理Zombie状态。
3.执行系统默认动作

我感觉1和3可以归为一类吧,只是有些signal没有系统默认动作,然后就跳过去了。一般的系统默认动作也就是终止进程。

SIGKILL/SIGSTOP不能被忽略的原因我觉得讲的不错,提供超级用户终止进程的可靠方法,不然后续有可能进程的行为是未定义的.

signal函数定义:(

void (*signal(int signo, void(*func)(int)))(int);

这个C函数的声明有点难看懂

APUE讲signal的语义与实现有关,所以建议大家都用sigaction

不过我看了下Android Bionic下的实现,分别包含BSD以及SYSV的版本,但也都变成了sigaction,所以一般在C库中就保证了这一点,也就无所谓了。

 

static  __sighandler_t
_signal(
int   signum, __sighandler_t  handler,  int   flags)
{
    
struct sigaction  sa;
    __sighandler_t    result 
= SIG_ERR;

    sigemptyset( 
&sa.sa_mask );

    sa.sa_handler 
= handler;
    sa.sa_flags   
= flags;

    
if ( !sigaction( signum, &sa, &sa ) )
        result 
= (__sighandler_t) sa.sa_handler;

    
return result;
}



__sighandler_t bsd_signal(
int  signum, __sighandler_t handler)
{
  
return _signal(signum, handler, SA_RESTART);
}


__sighandler_t sysv_signal(
int  signum, __sighandler_t handler)
{
  
return _signal(signum, handler, SA_RESETHAND);
}



exec函数会讲信号处理方式还原为系统默认,这个应该毫无疑问,exec后地址空间就不一样了,保留之前的捕捉函数也是无意义的;在这之前是有意义的,我的意思是说fork后exec之前。

后面有列一大堆是否可以重入的函数,不太想记,然后讲到了malloc
我想不能重入的函数无外乎两种类型
one: 自己有maintain一些全局or static变量 --> malloc 维护分配内存时static heap linklist
two: 函数参数里面有引用之类的,会影响调用者的情况。

然后我看到这个立马就想到了线程安全,malloc是线程安全的麽?
去看了下Bionic的实现,然后就又看到下面这个名字 Dong Lea,马上就会想到Java Concurrent库,看到这个你直接就会有想法,Bionic里面这个malloc肯定是线程安全的;
有时候被这些东西搞的很累,比较烦他,而且Bionic里面用的malloc的那个实现版本(aka dlmalloc)我又没看懂:(,只好去网上search了一下,有个人做过实验,有些结论还是可以参考的
http://www.360doc.com/content/12/0420/23/168576_205320609.shtml

总的来说,这个东西取决于C库实现,我想Bionic和Glibc都应该一样会支持两种不同的版本,然后编译的时候就可以确定是否有线程相关操作,然后在link的时候link过来不同的版本

线程安全和信号安全是两个概念

如果在线程安全的malloc中,信号处理函数中发生重入,那么应该是会发生dead lock
如果是非线程安全中,那么应该是所谓的 undefined behavior.

前面还有一个概率忘记写
早期的Unix系统,如果系统在执行一个低速系统调用(基本可以总结为blocking IO:包括IPC,File IO,ioctl),那么如果捕捉到信号,那么系统就会中断这个system call -->EINTR, 这在当时是有理由的,而且理由看起来也合理,但是由于user有时候并不知道某些系统调用是否是低速系统调用,BSD引进了自动重启的功能,linux follow这种规则

然后有几个版本的signal函数
signal默认自动重启
_signal(signum, handler, SA_RESTART) 这是由于在sigaction中的flag为SA_RESTART,上面sysV的实现我不想多写了。
sigaction也就是可选的了。

但一般我印象中好多 read/write都是会自己判断返回值以及errno 是否为 EINTR,然后retry,因为上面这种方式依赖系统实现,需要将所有的signal都设定为SA_RESTART,那么如果有某个信号发生的时候,被他INTR的系统调用才会自动重启,不知道默认signal在注册处理行为的时候是不是如此,感觉不太好用。

alarm函数
对于很多像alarm这种函数,在设计时or使用时均应该考虑临界值的问题
alarm的唯一性,是by process的,process单例
所以如果alarm两次,那么第一次会被覆盖,怎么处理第一次未完成的情况->返回值会带回来剩余的时间
怎么取消设定的alarm
传入值为0 means cancel
总之了,这个东西设计的时候还蛮完善的

后面还有一堆有关signal的标准函数,这里也就不一一列举

作业:
1.去掉for(;;), 那捕捉到SIGUSR1就返回了,pause()只要捕捉到信号,等信号处理完时自己就会返回并带回EINTR。不晓得为啥有这种题目...
2.实现sig2str,这有点无聊了.:(不写
3.画runtime stack的样子。
4.IO操作的超时最好不要采用alarm的方式,各种原子问题,select/poll是最好的选择。
后面有好一些是要写代码的...
我略想,然后就不太愿意写了.
PS:有些东西略难...我也是有点想不清楚的样子,打算慢慢搞。

后面决定把标题改一下,顺利看完书吧...Orz.

你可能感兴趣的:(LTZ做作业之APUE8)