进程间通信的信号
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程间通信的信号相关的知识,希望对你有一定的参考价值。
参考技术A信号(Signals )是Unix系统中使用的最古老的进程间通信的方法之一。操作系统通过信号来通知进程系统中发生了某种预先规定好的事件(一组事件中的一个),它也是用户进程之间通信和同步的一种原始机制。一个键盘中断或者一个错误条件(比如进程试图访问它的虚拟内存中不存在的位置等)都有可能产生一个信号。Shell也使用信号向它的子进程发送作业控制信号。
信号是在Unix System V中首先引入的,它实现了15种信号,但很不可靠。BSD4.2解决了其中的许多问题,而在BSD4.3中进一步加强和改善了信号机制。但两者的接口不完全兼容。在Posix 1003.1标准中做了一些强行规定,它定义了一个标准的信号接口,但没有规定接口的实现。目前几乎所有的Unix变种都提供了和Posix标准兼容的信号实现机制。
一、 在一个信号的生命周期中有两个阶段:生成和传送。当一个事件发生时,需要通知一个进程,这时生成一个信号。当进程识别出信号的到来,就采取适当的动作来传送或处理信号。在信号到来和进程对信号进行处理之间,信号在进程上挂起(pending)。
内核为进程生产信号,来响应不同的事件,这些事件就是信号源。主要的信号源如下:
异常:进程运行过程中出现异常;
其它进程:一个进程可以向另一个或一组进程发送信号;
终端中断:Ctrl-C,Ctrl-\\等;
作业控制:前台、后台进程的管理;
分配额:CPU超时或文件大小突破限制;
通知:通知进程某事件发生,如I/O就绪等;
报警:计时器到期。
在 Linux 中,信号的种类和数目与硬件平台有关。内核用一个字代表所有的信号,每个信号占一位,因此一个字的位数就是系统可以支持的最多信号种类数。i386 平台上有32 种信号,而Alpha AXP 平台上最多可有 64 种信号。系统中有一组定义好的信号,它们可以由内核产生,也可以由系统中其它有权限的进程产生。可以使用kill命令列出系统中的信号集。
下面是几个常见的信号。
SIGHUP: 从终端上发出的结束信号;
SIGINT: 来自键盘的中断信号(Ctrl-C);
SIGQUIT:来自键盘的退出信号(Ctrl-\\);
SIGFPE: 浮点异常信号(例如浮点运算溢出);
SIGKILL:该信号结束接收信号的进程;
SIGALRM:进程的定时器到期时,发送该信号;
SIGTERM:kill 命令发出的信号;
SIGCHLD:标识子进程停止或结束的信号;
SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号;
…………
每一个信号都有一个缺省动作,它是当进程没有给这个信号指定处理程序时,内核对信号的处理。有5种缺省的动作:
异常终止(abort):在进程的当前目录下,把进程的地址空间内容、寄存器内容保存到一个叫做core的文件中,而后终止进程。
退出(exit):不产生core文件,直接终止进程。
忽略(ignore):忽略该信号。
停止(stop):挂起该进程。
继续(continue):如果进程被挂起,则恢复进程的运行。否则,忽略信号。
进程可以对任何信号指定另一个动作或重载缺省动作,指定的新动作可以是忽略信号。进程也可以暂时地阻塞一个信号。因此进程可以选择对某种信号所采取的特定操作,这些操作包括:
忽略信号:进程可忽略产生的信号,但 SIGKILL 和 SIGSTOP 信号不能被忽略,必须处理(由进程自己或由内核处理)。进程可以忽略掉系统产生的大多数信号。
阻塞信号:进程可选择阻塞某些信号,即先将到来的某些信号记录下来,等到以后(解除阻塞后)再处理它。
由进程处理该信号:进程本身可在系统中注册处理信号的处理程序地址,当发出该信号时,由注册的处理程序处理信号。
由内核进行缺省处理:信号由内核的缺省处理程序处理,执行该信号的缺省动作。例如,进程接收到SIGFPE(浮点异常)的缺省动作是产生core并退出。大多数情况下,信号由内核处理。
需要指出的是,对信号的任何处理,包括终止进程,都必须由接收到信号的进程来执行。而进程要执行信号处理程序,就必须等到它真正运行时。因此,对信号的处理可能需要延迟一段时间。
信号没有固有的优先级。如果为一个进程同时产生了两个信号,这两个信号会以任意顺序出现在进程中并会按任意顺序被处理。另外,也没有机制用于区分同一种类的多个信号。如果进程在处理某个信号之前,又有相同的信号发出,则进程只能接收到一个信号。进程无法知道它接收了1个还是42个SIGCONT信号。
进程间通信—信号量
进程间通信——信号量(信号灯)
信号与信号量
信号:是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常,是一种处理异步事件的方式。
信号量:是一个特殊的变量,本质是计数器,记录了临界资源的数量。进程对其访问都是原子操作(PV操作),用于多线程、多进程之间同步临界资源。
信号量分类
按实现方式,信号量可以分为POSIX信号量与System V信号量。
System V信号量是基于内核维护的,,通常用于Linux系统中。Posix是由文件系统中的路径名对应的名字来标识的。
基于内存的信号量,同步多线程时,可放到该多线程所属进程空间里;如果是同步多进程,那就要把信号量放入到共享内存中(方便多个进程访问)。
System V 信号量使用步骤
1 打开/创建信号量 semget 2 信号量初始化 semctl 3 P/V操作 semop 4 删除信号量 semctl
1、信号量创建/打开---semget
1 #include <sys/ipc.h> 2 #include <sys/sem.h> 3 4 int semget(key_t key, int nsems, int semflg); 5 //成功返回信号量id,失败返回-1 6 //参数 7 //key---和消息队列关联的key, IPC_PRIVATE或ftok 8 //nsems---集合中包含计数信号量个数 9 //semflg---标志位, IPC_CREAT|0666 IPC_EXCL
2、信号量初始化---semctl
1 #include <sys/ipc.h> 2 #include <sys/sem.h> 3 4 int semctl(int semid, int semnum, int cmd, ...); 5 6 //成功返回0,失败返回EOF 7 //semid---要操作的信号量集的id 8 //semnum---要操作的集合中的信号量编号 9 //cmd---执行的操作 SETVAL IPC_RMID 10 //union semun---联合体,取决于cmd
union semun
1 union semun { 2 int val; //SETVAL的值 3 struct semid_ds *buf; //Buffer for IPC_STAT, IPC_SET 4 unsigned short *array; //Array for GETALL, SETALL 5 struct seminfo *_buf; //Buffer for IPC_INFO 6 }
3、信号量P/V操作---semop
1 #include <sys/ipc.h> 2 #include <sys/sem.h> 3 4 int semop(int semid, struct sembuf *sops, unsigned nsops); 5 6 //成功返回0,失败EOF 7 //参数 8 //semid---要操作的信号量集的id 9 //sops ---描述对信号量操作的结构体(数组) 10 //nsops---要操作的信号量的个数 11 12 struct sembuf{ 13 short sem_num; //信号量编号 14 short sem_op; //-1:P操作,1:V操作 15 short sem_flg; //SEM_UNDO即0 ,IPC_NOWAIT
16 }
通常为SEM_UNDO即0,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,
操作系统释放信号量
示例:信号量集/共享内存
要求:父子进程通过System V信号量同步对共享内存的读写
父进程从键盘输入字符串
子进程输出字符串
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/ipc.h> 5 #include <errno.h> 6 #include <sys/sem.h> 7 #include <string.h> 8 9 union semun{ 10 int val; 11 } 12 13 #define SEM_READ 0 14 #define SEM_WRITE 0 15 16 //实现P操作 17 poperation(int index, int semid) //传入要操作的信号量,信号量集 18 { 19 //初始化sembuf结构体 20 struct sembuf sop; 21 sop.sem_num = index; //信号量编号 22 sop.sem_op = -1; //P操作:-1 23 sop.sem_flg = 0; //SEM_UNDO 24 25 //信号量集ID,对信号量操作的结构体,操作的信号量个数 26 semop(semid,&sop,1); 27 } 28 29 voperation(int index, int semid) 30 { 31 //初始化sembuf结构体 32 struct sembuf sop; 33 sop.sem_num = index; //信号量编号 34 sop.sem_op = 1; //V操作:1 35 sop.sem_flg = 0; //SEM_UNDO 36 37 //信号量集ID,对信号量操作的结构体,操作的信号量个数 38 semop(semid,&sop,1); 39 40 } 41 42 int main() 43 { 44 key_t key; 45 int semid; //信号量集ID 46 int shmid; //共享内存ID 47 char * shmaddr; 48 pid_t pid; 49 50 key = ftok(".",123); //创建key键值,进而生成不同的IPC对象的标识符ID 51 52 //创建2个信号量 53 semid = semget(key, 2, IPC_CREAT|0777); 54 if(semid < 0) 55 { 56 perror("semid"); 57 return -1; 58 } 59 60 //创建共享内存 61 shmid = shmget(key, 256, IPC_CREAT|0777); 62 if(shmid < 0) 63 { 64 perror("shmget"); 65 return -1; 66 } 67 68 //初始化2个信号量 69 union semun myun; 70 myun.val = 0; //初始化读信号量的值为0,未读 71 semctl(semid, SEM_READ, SETVAL, myun); 72 73 myun.val = 1; //初始化写信号量的值为1,可写 74 semctl(semid, SEM_WRITE,SETVAL, myun); 75 76 //创建一个子进程 77 pid = fork(); 78 if(pid < 0) 79 { 80 perror("fork"); 81 return -1; 82 } 83 else if(pid == 0) //子进程 84 { 85 //获取映射后的共享内存的地址 86 shmaddr = (char *)shmat(shmid, NULL, 0); 87 88 poperation(SEM_READ,semid); //读之前对读信号量进行P操作 89 90 printf("getshm:%s",shmaddr); 91 92 voperation(SEM_WRITE,semid); //V操作,读完了,告诉父进程可写 93 94 }else{ //父进程从键盘输入字符 95 96 //获取映射后的共享内存的地址 97 shmaddr = (char *)shmat(shmid, NULL, 0); 98 99 poperation(SEM_WRITE,semid); //写之前对写信号量进行P操作 100 101 printf("Please input char to shm:"); 102 fgets(shmaddr, 32,stdin); 103 104 voperation(SEM_READ,semid); //V操作,写完,告诉子进程可以读 105 } 106 107 108 }
结果:
【信号量的意图在于进程间同步,互斥锁和条件变量的意图则在于线程间同步。但是信号量也可用于线程间,互斥锁和条件变量也可用于进程间。我们应该使用适合具体应用的那组原语。】--------https://www.cnblogs.com/fangshenghui/p/4039946.html
以上是关于进程间通信的信号的主要内容,如果未能解决你的问题,请参考以下文章