对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
信号概述
信号的名字和编号:
每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。

//1)挂起    2)中断(ctl+c)  3)退出    4)出现了问题   1) SIGHUP  2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP//6)丢弃    7)总线信号                9)杀死进程6) SIGABRT   7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1//                                   14)闹钟信号
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
//                                        19)停止程序
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
//                                          29)IO口的访问
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

信号的处理:
      信号的处理有三种方法,分别是:忽略、捕捉和默认动作
      忽略信号:大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
      捕捉信号:需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
      系统默认动作:对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。(kill -9 pid 或者 kill -SIGKILL pid)杀死进程
具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。
信号处理函数的注册
信号处理函数的注册不只一种方法,分为入门版和高级版
(1)入门版:函数signal
(2)高级版:sigqueue
信号发送函数也不止一个,同样分为入门版和高级版
(1)入门版:kill
(2)高级版:sigqueue
signal

#include <signal.h>
typedef void (*sighandler_t)(int);//这个是函数指针,没有返回数,参数是int型,sighandler_t是函数名
sighandler_t signal(int signum, sighandler_t handler);//返回值是sighandler_t,第一个参数signum是要捕捉哪些信号,第二个参数handler是一种类型,指向函数指针void (*sighandler_t)(int)

代码演示

#include <signal.h>
#include<stdio.h>
// typedef void (*sighandler_t)(int);// sighandler_t signal(int signum, sighandler_t handler);void handler(int signum)//信号处理函数,捕捉信号
{printf("get signal=%d\n",signum);switch(signum){case 2:printf("SIGINT\n");break;case 9:printf("SIGKILL\n");case 10:printf("SIGUSER1\n");}printf("never quite\n");
}
int main()
{signal(SIGINT,handler);//如果将handler改为SIG_ICN即可忽略函数signal(SIGKILL,handler);signal(SIGUSR1,handler);while(1);
}

kill发送消息——低级版

 #include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);

代码实战

#include <signal.h>
#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>int main(int argc,char**argv)
{int signum;int pid;char cmd[128]={0};signum=atoi(argv[1]);pid=atoi(argv[2]);//atoi将阿斯科码转化为整数printf("num=%d,pid=%d\n",signum,pid);
//      kill(pid,signum);//用来发信号sprintf(cmd,"kill -%d %d",signum,pid);//cmd是目标字符串,第二个参数是想要的目标字符串的长相system(cmd);//用system调用脚本发信号printf("send signal ok\n");return 0;
}

sigaction原型(安装信号处理程序)

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
//第一个参数是信号,第二个是sigaction结构体用来绑定某些参数,第三个参数也是sigaction结构体,用来备份原有的信号操作,如不需要则设为NULL
struct sigaction {void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用,第一个参数是信号处理函数,第三个是指针空无数据,非空有数据sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据};
//回调函数句柄sa_handler、sa_sigaction只能任选其一

关于void (*sa_sigaction)(int, siginfo_t *, void );处理函数来说还需要有一些说明。void 是接收到信号所携带的额外数据;而struct siginfo这个结构体主要适用于记录接收信号的一些相关信息。

 siginfo_t {int      si_signo;    /* Signal number */int      si_errno;    /* An errno value */int      si_code;     /* Signal code */int      si_trapno;   /* Trap number that causedhardware-generated signal(unused on most architectures) */pid_t    si_pid;      /* Sending process ID */uid_t    si_uid;      /* Real user ID of sending process */int      si_status;   /* Exit value or signal */clock_t  si_utime;    /* User time consumed */clock_t  si_stime;    /* System time consumed */sigval_t si_value;    /* Signal value 是结构体*/int      si_int;      /* POSIX.1b signal */void    *si_ptr;      /* POSIX.1b signal */int      si_overrun;  /* Timer overrun count; POSIX.1b timers */int      si_timerid;  /* Timer ID; POSIX.1b timers */void    *si_addr;     /* Memory location which caused fault */int      si_band;     /* Band event */int      si_fd;       /* File descriptor */
}

其中的成员很多,si_signo 和 si_code 是必须实现的两个成员。可以通过这个结构体获取到信号的相关信息。
信号发送函数——高级版

#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);//第一个是发给谁,第二个是发的什么信号,第三个是消息
union sigval {int   sival_int;void *sival_ptr;};

接收端代码实战

#include <signal.h>
#include<stdio.h>// int sigaction(int signum, const struct sigaction *act,  struct sigaction *oldact);
void handler(int signum, siginfo_t * info,void * context)
{printf("get signum:%d\n",signum);if(context!=NULL){printf("get data=%d\n",info->si_int);printf("get data=%d\n",info->si_value.sival_int);printf("from: %d\n",info->si_pid);}}
int main()
{struct sigaction act;printf("ps=%d\n",getpid());act.sa_sigaction=handler;//收到信号后调用handle处理信号act.sa_flags=SA_SIGINFO;//SA_SIGINFO表示能接收数据sigaction(SIGUSR1,&act,NULL);while(1);return 0;
}

发送端代码实战

#include<stdio.h>
#include <signal.h>
//int  sigqueue(pid_t  pid,  int  sig,  const  union  sigval value);
int main(int argc,char**argv)
{int signum;int pid;signum=atoi(argv[1]);pid=atoi(argv[2]);union sigval value;value.sival_int=100sigqueue(pid,signum,value);printf("pid =%d\n",getpid());printf("over\n");       return 0;
}

临界资源:
多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。
信号量
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
特点

1、 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。

2、信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

3、 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

4、 支持信号量组。
原型:
      最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。
      Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

#include <sys/sem.h>
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1int semget(key_t key, int num_sems,int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);  // 控制信号量的相关信息int semctl(int semid, int sem_num, int cmd, ...);

当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1; 如果是引用一个现有的集合,则将num_sems指定为 0 。

代码实战

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
//     int semget(key_t key, int nsems, int semflg);
//int semctl(int semid, int semnum, int cmd, ...);
//int semop(int semid, struct sembuf *sops, unsigned nsops);//取钥匙的函数,第一个参数是信号量ID,第二个是配置信号量的个数,第三个是第二项的个数
union semun {int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};
void vPutBackKey(int id)
{struct sembuf set;set.sem_num=0;set.sem_op=+1;set.sem_flg=SEM_UNDO;semop(id,&set,1);printf("put back key\n");
}
void pGetKey(int id)
{struct sembuf set;set.sem_num=0;//信号量的编号set.sem_op=-1;//拿钥匙后原来的钥匙减一set.sem_flg=SEM_UNDO;//SEM_UNDO表示等待,IPC_NOWAIT表示不等待semop(id,&set,1);printf("get key\n");
}
int main (int argc,char**argv)
{int semid;key_t key=ftok(".",2);semid=semget(key,1,IPC_CREAT|0666);//第二个参数代表信号量集所含信号量个数,flag表示如果没有信号怎么做。IPC_CREAT表示若信号量不存在则创建它,0666表示信号量的权限union semun initsem;initsem.val=0;//刚开始里面是没有钥匙的状态semctl(sempid,0,SETVAL,initsem);//将信号量初始化,第一个参数是信号量集的ID,第二个参数是代表要操作第几个信号量 ,0代表第0个。第三个参数是要对信号量如何操作,SETVAL表示设置信号量的值设置为initsem,initsem.val=0表示有一把钥匙。pid_t pid=fork();if(pid>0){pGetKey(semid);printf("this is father\n");vPutBackKey(semid);semctl(semid,0,IPC_RMID);//销毁信号量}else if(pid==0){//pGetKey(semid);printf("this is child\n");vPutBackKey(semid);}else{printf("fork error\n");}return 0;
}

本文参考以下两篇博文编写:
信号量
信号

linux 信号和信号量编程相关推荐

  1. linux 信号_Linux信号量(1)-SYSTEM V

    ​ 信号量概念 信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送 ...

  2. Linux 信号学习

    Linux 信号学习 信号量的基本概念 信号产生的条件 信号如何被处理 信号的异步特质 信号的分类 可靠信号/不可靠信号 实时信号/非实时信号 常见信号与默认行为 信号处理 `signal()` 函数 ...

  3. Linux进程间通信(管道、消息队列、共享内存、信号、信号量)

    目录 Linux进程间通信概述 1.管道 无名管道(pipe) 有名管道(fifo) 2.消息队列(msg) 消息队列的通信原理 消息队列相关api 消息队列收发数据 键值生成 消息队列移除 3.共享 ...

  4. 【Linux系统编程】Linux信号列表

    00. 目录 文章目录 00. 目录 01. Linux信号编号 02. 信号简介 03. 特殊信号 04. 附录 01. Linux信号编号 在 Linux 下,每个信号的名字都以字符 SIG 开头 ...

  5. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux信号机制分析

    Linux信号机制分析 Sailor_forever  sailing_9806@163.com 转载请注明 http://blog.csdn.net/sailor_8318/archive/2008 ...

  6. 【Linux内核及驱动编程】Linux信号机制分析

    Linux信号机制分析 Sailor_forever sailing_9806@163.com转载请注明 http://blog.csdn.net/sailor_8318/archive/2008/0 ...

  7. 新书预告:《Linux 多线程服务端编程——使用 muduo C++ 网络库》

    看完了 W. Richard Stevens 的传世经典<UNIX 网络编程>, 能照着例子用 Sockets API 编写 echo 服务, 却仍然对稍微复杂一点的网络编程任务感到无从下 ...

  8. Linux设备驱动开发详解-Note(5)---Linux 内核及内核编程(1)

    Linux 内核及内核编程(1) 成于坚持,败于止步 Linux 2.6 内核的特点 Linux 2.6 相对于 Linux 2.4 有相当大的改进,主要体现在如下几个方面. 1.新的调度器 2.6 ...

  9. linux环境c语言编程 蔡晋,Linux环境C语言编程

    Linux环境C语言编程第1讲linux系统环境介绍 Linux环境C语言编程第2讲命令行解析+环境变量+gcc基本参数 Linux环境C语言编程第3讲共享库.gdb的使用 Linux环境C语言编程第 ...

最新文章

  1. 酒店用机器学习,预测哪些客人会放鸽子
  2. 特征因子是什么意思_生态因子作用的一般特征
  3. 一步一步CCNA之六:交换机vtp配置
  4. IE8 chrome 中 table隔行换色解决办法
  5. idea maven项目下载源码及关联源码
  6. 打造 通用的 支持多数据库 操作的 DBHelper
  7. cocos ScrollView(滚动容器)加载大量item导致的卡顿问题解决方案
  8. 前端学习(2781):底部tabber配置
  9. 【STM32】【STM32CubeMX】STM32CubeMX的使用之三:UART串口通信
  10. QuantLib 金融计算——QauntLib 入门
  11. Linux下MongoDB服务安装
  12. spring中redistemplate不能用通配符keys查出相应Key的问题
  13. xp系统设置锁定计算机,XP系统电脑如何设置自动锁屏?
  14. python3d旋转相册,用HTML+CSS代码制作3D旋转相册
  15. 如何批量压缩图片体积大小kb?
  16. 【机器学习笔记】【决策树】【泰坦尼克号幸存者的预测】
  17. Pytorch 实现强化学习策略梯度Reinforce算法
  18. 计算机专业前端实习生的实习经历
  19. get php 怎么用,php get用法是什么
  20. 树莓派摄像头测距程序

热门文章

  1. Pytorch基础(十)——优化器(SGD,Adagrad,RMSprop,Adam,LBFGS等)
  2. amazons3 检查连接是否_钢筋机械连接接头如何检查是否合格?抽检数量、合格率是多少?...
  3. 灰度图像的8位平面分解
  4. C++类的内联成员函数应放在哪
  5. 文字转语音+html5,JS实现文字转语音并播放
  6. python的基础网络编程是下列_Python入门基础之网络编程、socket编程、TCP、UDP编程...
  7. c语言中for语句的作用是,c语言中for语句是怎么用的
  8. python如何跳出外层循环_失去循环标签的Python,我这样实现跳出外层循环
  9. Laravel笔记记录
  10. 130242014022 蓝宏铮 第2次实验