linux下进程的信号量

linux下进程的信号量

信号:

  1. 信号的基本概念
  2. 信号与异常处理
  3. 信号的处理方法
  4. 信号的可靠性
  5. 函数可重入性
  6. 信号集
  7. 信号屏蔽

首先来介绍一下什么是信号,信号的基本概念

1:信号(signal)机制是Linux系统中最为古老的进程之间的通信机制解决进程在正常运行过程中被中断的问题,导致进程的处理流程会发生变化
2:信号是软件中断
3:信号是异步事件

a:不可预见
b:信号有自己的名称和编号
c:信号和异常处理机制

4:信号发生的来源

a:硬件来源,如按键的事件,或者硬件故障,是由驱动程重点内容序产生
b:软件来源:最常用发送信号的系统函数 kill(),raise(),alarm()和setitimer()等函数
软件来源还包括一些非法运算等操作,软件设置条件如(gdb调试),信号由内核产生;

5:查看linux系统内置的信号,在前面博客中其实也提到相关信号:

linux信号类型

使用kill -l来查看信号列表
信号是没有优先级之分的;
1-31称为非实时信号,发送的信号可能会丢失,不支持信号排队,
32-64成为实时信号,支持信号排队,发送的多个实时信号都会被接受

前面31个信号类型在linux中的/usr/include/bits/signum.h文件中会有详细定义

6: 信号的历史变革

    信号出现在早期的Unix中
    早期信号模型是不可靠的
    BSD和System V分别对早期信号进行扩展,但是相互不兼容
    POSIX统一了上述两种模型,提供了可靠信号模型

信号的处理方式:

进程可以通过三种方式来响应和处理一个信号
    1:忽略信号
        SIGKILL和SIGSTOP永远不能忽略
        忽略硬件异常
        进程启动时SIGUSR1和SIGUSR2两个信号会被忽略

    2:执行默认操作
        每个信号有默认动作,大部分信号默认动作都是终止进程 

    3:捕获信号
        告诉内核出现信号时调用自己的处理函数
        SIGKILL和SIGSTOP不能被捕获

信号的处理方法

信号注册(接收)函数
1. signal函数

signal函数

#include<signal.h>
void (*signal(int signo,void(*func)(int))) (int);

返回:若成功则返回先前的信号处理函数指针(func),出错返回SIG_ERR
功能:向内核登记信号处理函数
参数:
    signo
        要登记的信号值,一般用信号的宏来
    func
        信号处理函数指针
        SIG_IGN
            忽略信号
        SIG_DFL
            采用系统默认的方式处理信号,执行默认操作            
使用man signal可以查看这个函数的具体的函数定义

非常简单的实例代码

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>


void signal_handler(int signo){
    printf("signo:%d\n",signo);
}
int main(int agrc,char * argv[]){
    //ctrl+c    
    if(signal(SIGINT,signal_handler) == SIG_ERR){
        perror("sigint error");
    }

    if(signal(SIGSEGV,signal_handler) == SIG_ERR){
        perror("sigsegv error");
    }

    //SIGUSR1,SIGUSR2在不捕获的状态下,默认是忽略的
    if(signal(SIGUSR2,signal_handler) == SIG_ERR){
        perror("siguser1 error");
    }

    if(signal(SIGUSR1,signal_handler) == SIG_ERR){
        perror("siguser2 error");
    }
    //sigkill和sigstop是不允许被忽略和捕获的,在注册的时候就已经会报错
    if(signal(SIGKILL,SIG_DFL) == SIG_ERR){
        perror("sigkill error");
    }

    if(signal(SIGSTOP,SIG_DFL) == SIG_ERR){
        perror("signal stop error");
    }
    //ctrl+z    
    if(signal(SIGTSTP,signal_handler) == SIG_ERR){
        perror("signal tstp error");
    }
    int i = 0;
    while(++i < 50){
        printf("pid:%d,count:%d\n",getpid(),i);
        sleep(1);
    }

    return 0;
}

怎么使用信号来处理僵尸进程

 SIGCHILD信号
    子进程状态发生改变(子进程结束)产生该信号,父进程需要使用wait调用来等待子进程结束并回收它。
    避免僵尸进程

子进程结束之后自动产生的该信号;
当父进程捕获到SIGCHILD信号之后,一定要去调用wait(0)函数,否则子进程会成为僵尸进程
优点:只有当子进程终止之后,父进程才调用wait函数,而如果父进程直接去调用wait函数的话,那这个时候可能就会导致父亲进程会直接阻塞住。这样就会导致父进程执行的效率极低

父亲进程通过注册去监听回收僵尸进程部分的代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/types.h>

void singal_handler(int signo){
    printf("%d occred %d\n",getpid(),signo);
    //释放子进程(僵尸进程)
    wait(NULL);
}

void outNum(int num){
    int i = 0;
    for(i = 0;i<num;i++){
        printf("%d count %d\n",getpid(),i);
        sleep(2);
    }
}
int main(int argc,char * argv[]){

    pid_t pid = fork();

    if(pid < 0){
        perror("fork error\n");
        exit(1);
    }else if(pid > 0){
        //注册等级子进程deaded的时候的信号
        if(signal(SIGCHLD,singal_handler)==SIG_ERR){
            perror("signal child error");
        }
        outNum(20);
    }else {
        outNum(10);
    }
    return 0;
}

信号注册(接收)函数
信号发送:

除了内核和超级用户,并不是每个进程都可以向其他进程发送信号
一般的进程只能向具有相同uid和gid的进程发送信号或相同进程组中的其他进程发送信号
常用的发送信号的函数有
1:kill(),
2:raise(),
3:alarm(),
4:setitimer(),
5:abort()等

1:kill函数和raise函数

#include<signal.h>
int kill(pid_t pid,int signo);
返回:成功返回0,出错返回-1
功能,向指定的进程发送某一个信号
int raise(int signo);
返回:成功返回0,出错返回-1
功能:向进程本身发送一个信号,相当于kill(getpid(),signo);
参数:
    pid 接受信号的pid
    signo 要发送的信号值

kill函数将信号发送给进程或者进程组
    0为空信号,常常用来检测特定的进程是否存在


参数pid取值:
    pid > 0 将信号发给进程ID为pid的进程
    pid==0将信号发给与发送进程同一进程组的所有进程
    pid<0将信号发送给进程组ID等于pid的绝对值
    pid==-1将信号发送给发送进程有权限向他们发送信号的系统上的所有进程

2:alarm函数:

#include<unistd.h>
unsigned int alarm(unsigned int seconds);
useconds_t ualarm(useconds_t usecs, useconds_t interval);


返回:0或以前设置的定时器时间余留的秒数
alarm函数可设置定时器 ,当定时器超时,产生一个SIGALRM信号
该信号由内核产生,在制定的seconds秒之后,给进程本身发送 一个SIGALRM信号
参数为0,取消以前设置的定时器
ualarm是微秒为单位的

上面信号发送的两个函数的调用其实也相对来说比较简单。在这里就不去进行演示了。
总结一下进程的信号量问题:
其实linux进程的信号量问题给我的感觉就好像是android里面的广播机制一样,如果你想要去接受到广播的话,那么就必须提前去注册一个广播的监听,这样才会有接受广播的资格,而进程信号其实也是,要想在进程中去监听信号的话,也必须要先通过singal去注册,而发广播其实就是linux进程信号的发送方式,可以通过kill,raise,等函数调用。去向指定进程(注册过信号的)发送消息。而指定进程根据消息类型类选择执行的方式。
谢谢大家的观看。写的不好的地方,希望能够给予指出。

一起交流学习的qq群号为324652573

欢迎持续访问博客

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值