linux 信号和信号量编程

对于 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}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/272487.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓动画基础讲解

//逐帧动画 /** * 1.加入单张图片 * 2.生成movie.xml整个图片 * 3.代码中使用图片movie.xml */ iv(ImageView) findViewById(R.id.iv);// iv.setImageResource(R.drawable.movie);//为iv加载六张图片// AnimationDrawable ad(AnimationDrawable) iv.getDrawable();//得到图片给…

JS一些常用的类库

一、返回上一页&#xff08;history&#xff09;发觉有两种用法&#xff1a;1、javascript:history.back(-1);2、javascript:history.go(-1);它们俩的区别是&#xff1a;history.back(-1):直接返回当前页的上一页&#xff0c;数据全部消息&#xff0c;返回新页面history.go(-1)…

Linux上线程开发API概要(线程)

进程与线程 典型的UNIX/Linux进程可以看成只有一个控制线程&#xff1a;一个进程在同一时刻只做一件事情。有了多个控制线程后&#xff0c;在程序设计时可以把进程设计成在同一时刻做不止一件事&#xff0c;每个线程各自处理独立的任务。 进程是程序执行时的一个实例&…

Redis学习笔记1-Redis数据类型

Redis数据类型 Redis支持5种数据类型&#xff0c;它们描述如下&#xff1a; Strings - 字符串 字符串是 Redis 最基本的数据类型。Redis 字符串是二进制安全的&#xff0c;也就是说&#xff0c;一个 Redis 字符串可以包含任意类型的数据&#xff0c;一个字符串最大为 512M 字节…

30个非常有趣的404错误页面设计欣赏

当用户访问一个不存在的页面的时候就会出现404错误页面&#xff0c;这对用户来说是很不友好的。所以很多网站都会去设计一个新颖的错误页面&#xff0c;以吸引用户继续浏览其它的网页内容。今天这篇文章就收集了30个非常有趣的404错误页面设计欣赏&#xff0c;希望能带给你灵感…

线程同步之互斥量加锁解锁 死锁

与互斥锁相关API 互斥量&#xff08;mutex&#xff09;从本质上来说是一把锁&#xff0c;在访问共享资源前对互斥量进行加锁&#xff0c;在访问完成后释放互斥量上的锁。对互斥量进行加锁后&#xff0c;任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互…

游戏开发-从零开始 002

个人开发者的游戏大部分需要完成的内容&#xff1a; 1.完整的游戏玩法逻辑&#xff08;核心&#xff09; 2.UI 3.游戏关卡设计 4.游戏旁白 5.交互细节 6.游戏分享接口 7.游戏道具 8.游戏排行榜&#xff0c;游戏社区&#xff0c;如 GameCenter 9.游戏内购 如 remove Ads 10.广告…

5 个最佳的 Linux 桌面环境

打算把每个桌面都试用一遍&#xff0c;但是那很费时间&#xff0c;而且确实有很多桌面环境可供选择&#xff0c;这就是我发表“最优秀的 Linux 桌面以及他们的优缺点”的目的&#xff0c;本文告诉你在选择桌面时需要注意些什么&#xff0c;让我们开始吧。1. KDE我想从第五个说起…

线程条件控制实现线程的同步

与条件变量相关API 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时&#xff0c;允许线程以无竞争的方式等待特定的条件发生。 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量&#xff0c…

自定义能够for each的类,C#,Java,C++,C++/cli的实现方法

自定义类能够被for each&#xff0c;应该算是个老生常谈的话题了&#xff0c;相关的资料都很多&#xff0c;不过这里整理总结主流语言的不同实现方式&#xff0c;并比较部分细节上的差异。 第一种语言&#xff0c;也是实现起来最简单的Java语言。在Java里&#xff0c;要被for e…

SQL Server 2008 R2:快速清除日志文件的方法

本例&#xff0c;快速清理“students”数据库的日志&#xff0c;清理后日志文件不足1M。USE [master] GO ALTER DATABASE students SET RECOVERY SIMPLE WITH NO_WAIT GO ALTER DATABASE students SET RECOVERY SIMPLE GO USE students GO--此处需要注意&#xff…

linux网络编程之字节序

进程间通信 特点&#xff1a;依赖于内核&#xff0c;造成缺陷——无法实现多机通信。 网络编程 地址&#xff1a;由IP地址和端口号构成&#xff0c;端口号用来判断客户端接入哪个服务器。 数据的交流&#xff1a;涉及到协议&#xff08;http&#xff0c;tcp&#xff0c;udp&…

Oracle查看表空间和表空间中的对象

select * from user_tables;--查询所有用户表 select username,default_tablespace from user_users;--查询当前表空间select tablespace_name from dba_tablespaces;--查询所有表空间select tablespace_name, sum(bytes)/1024/1024 from dba_data_files group by tablespace_n…

C#中DateTime.Ticks属性及Unix时间戳转换

DateTime.Ticks&#xff1a;表示0001 年 1 月 1 日午夜 12:00:00 以来所经历的 100 纳秒数&#xff0c;即Ticks的属性为100纳秒&#xff08;1Ticks 0.0001毫秒&#xff09;。Unix时间戳&#xff1a;是从1970年1月1日&#xff08;UTC/GMT的午夜&#xff09;开始所经过的秒数&am…

WebBrowser控件的常用方法、属性和事件

1. 属性属性说明Application如果该对象有效&#xff0c;则返回掌管WebBrowser控件的应用程序实现的自动化对象(IDispatch)。如果在宿主对象中自动化对象无效&#xff0c;这个程序将返回WebBrowser 控件的自动化对象Parent返回WebBrowser控件的父自动化对象&#xff0c;通常是一…

二维码高亮

// 二维码高亮。http://blog.sina.com.cn/s/blog_a843a8850102uy6w.html 转载于:https://www.cnblogs.com/muyushifang07/p/5114667.html

socket 网络 编程

网络编程场景 自己是客户端站在5栋楼前&#xff0c;自己要找到5栋楼中的一座并进入某一间房间&#xff0c;这时第二座楼上有人在用汉语&#xff08;tcp/udp&#xff09;说话,我的ip地址&#xff08;楼号&#xff09;是…&#xff0c;我的端口号&#xff08;房间号&#xff09;是…

7个免费的Linux FTP客户端工具

在Dropbox、YouSendIt、idrive以及许多这样云存储和共享工具的帮助下&#xff0c;我们在互联网上发送和共享大型文件变得容易起来。所有这些网站都可以帮助你在互联网上传送文件&#xff0c;但如果你要分享庞大的数据&#xff0c;这依然是很复杂的事情。所以&#xff0c;你需要…

树莓派的几种登录方式及树莓派的网络配置

&#xff08;1&#xff09;HDMI 视频线 连接到显示器 &#xff08;2&#xff09;串口 设备破解&#xff1a; 默认情况下,树莓派的串口和蓝牙连接&#xff0c;把串口用来数据通信。 修改系统配置&#xff0c;启用串口登录树莓派 1.打开SD卡根目录的"config.txt"文件…

C语言之常量与变量

1.常量 1.1整型常量:短整型(short int),整型(int),长整型(long int).短整型和长整型都可省虑后面的int,三者唯一的区别就是内存大小的区别,从小到大依次为short < int < long. int a;short int b;long int c;  printf("%d,%d",a,b);  printf("%ld&quo…