Linux中的进程信号

目录

进程信号

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);

子进程自动退出,不会在想父进程发送信号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值