目录
进程信号
kill/raise/abort
硬件异常产生信号
由软件条件产生信号
信号在内核中的表示示意图
pending:
block:
信号集操作函数
sigprocmask
进程信号
信号量和信号不同
信号量的本质是计数器,计数器可以被多进程同时看到。可以对资源进行预定。
所有进程在访问公共资源之前,必须先申请(sem)信号量
必须申请信号量的前提是所有进程必须的看到同一个信号量
信号量本身也是公共资源,所以信号量必须保证自身操作的安全性,++,--都必须是原子操作。
共享内存,消息队列,信号量中均有:
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
结构体的第一个成员的地址,在数字上,和结构体对象本身的地址数字是相等的,虽然类型不同。
进程收到信号时信号不一定会被立即处理,所以进程应有对信号的保存能力(保存在task_struct->收到信号时将对应的位图结构有0->1)。
发送信号的本质就是修改PCB中的信号位图。
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1。
kill/raise/abort
kill():可以向任意进程发送任意信号
kill xxx x
raise():给自己发送任意信号
raise(x);
abort():给自己发送指定信号SIGABRT
abort();
man 7 signal
Signal Standard Action Comment
────────────────────────────────────────────────────────────
SIGABRT P1990 Core Abort signal from abort(3)
SIGALRM P1990 Term Timer signal from alarm(2)
SIGBUS P2001 Core Bus error (bad memory access)
SIGCHLD P1990 Ign Child stopped or terminated
SIGCLD - Ign A synonym for SIGCHLD
SIGCONT P1990 Cont Continue if stopped
SIGEMT - Term Emulator trap
SIGFPE P1990 Core Floating-point exception
硬件异常产生信号
硬件CPU中有mmu内存管理单元,当有越界访问时mmu会异常。OS会识别并向目标进程发送11号信号(野指针)。
由软件条件产生信号
SIGPIPE是一种由软件条件产生的信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动
作是终止当前进程。
core为核心转储,当进程出现异常时,将进程在对应时刻,在内存中的有效数据转储到磁盘中----核心转储->可以支持调试。
core-file core.xxx
实际执行信号的处理动作称为信号递达(Delivery)
信号从产生到递达之间的状态,称为信号未决(Pending)。
进程可以选择阻塞 (Block )某个信号。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号在内核中的表示示意图
置于pending表中的信号其状态为未决状态。
pending:
pending位图中,比特位的位置为信号编号,比特位的内容表示是否收到了对应的信号
block:
block位图中,比特位的位置为信号编号,比特位的内容为是否阻塞了对应的信号。
如果一个信号没有被产生,并不妨碍其可以先被阻塞。
task_struct中有字段handler_t handler[32] ,handler数组是有下标的
a.数组的位置(下标),信号的编号。
b.数组下标对应的内容,表示对应信号的处理方法。
sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号 的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。
信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
sigprocmask
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1
#define BLOCK_SIGNAL 2
#define MAX_SIGNUM 31
static void show_pending(const sigset_t& pending)
{
for(int signo=MAX_SIGNUM;signo>=1;--signo)
{
if(sigismember(&pending,signo))
cout<<"1";
else cout<<"0";
}
cout<<"\n";
}
int main()
{
sigset_t block,oblock,pending;
sigemptyset(&block);
sigemptyset(&oblock);
//sigemptyset(&pending);
sigaddset(&block,BLOCK_SIGNAL);
sigprocmask(SIG_SETMASK,&block,&oblock);
while (true)
{
sigemptyset(&pending);
sigpending(&pending);
show_pending(pending);
sleep(1);
}
}
0000000000000000000000000000000
0000000000000000000000000000000
^C
0000000000000000000000000000010
一旦对特点信号进行解除屏蔽,一般OS至少立马递达一个信号。
当某一个信号正在被递达期间,同类型信号无法递达!
当当前信号正在被捕捉时,系统会自动将当前信号加入信号屏蔽字。
当信号完成捕捉动作,系统又会自动解除对该信号的屏蔽。
void handler(int signo)
{
printf("pid: %d, %d 号信号,正在被捕捉!\n",getpid(),signo);
}
void Count(int cnt)
{
while (cnt)
{
printf("cnt: %2d\n",cnt);
fflush(stdout);
cnt--;
sleep(1);
}
printf("\n");
}
signal(SIGCHLD,handler);
printf("父进程, %d, ppid: %d\n",getpid(),getppid());
pid_t id=fork();
if(id==0)
{
printf("子进程, %d,ppid: %d, exit\n",getpid(),getppid());
Count(5);
exit(0);
}
while (1)
{
sleep(1);
}
子进程退出时会向父进程发送17号信号。
pid_t ret=waitpid(-1,null,WNOHANG);
非阻塞式的等待子进程退出。
signal(SIGCHLD,SIG_IGN);
子进程自动退出,不会在想父进程发送信号。