Linux_信号与信号量【转】
转自:http://blog.csdn.net/sty23122555/article/details/51470949
信号:
信号机制是类UNIX系统中的一种重要的进程间通信手段之一。我们经常使用信号来向一个进程发送一个简短的消息。例如:假设我们启动一个进程通过socket读取远程主机发送过来的网络数据包,此时由于网络因素当前主机还没有收到相应的数据,当前进程被设置为可中断等待状态(TASK_INTERRUPTIBLE),此时我们已经失去耐心,想提前结束这个进程,于是可以通过kill命令想这个进程发送KILL信号,内核会唤醒该进程,执行它的信号处理函数,KILL信号的默认处理是退出该进程。
另外应用程序可以通过signal()等函数来为一个信号设置默认处理函数。例如当用户按下CTRL+C时,shell将会发出SIGINT信号,SIGINT的默认处理函数是执行进程的退出代码,如下所示:
可以通过类似下面的命令显式的给一个进程发送一个信号:
kill -2 pid
事实上,进程也不知道信号到底什么时候到达。信号是异步的,一个进程不可能等待信号的到来,也不知道信号会到来,那么,进程是如何发现和接受信号呢?实际上,信号的接收不是由用户进程来完成的,而是由内核代理。当一个进程P2向另一个进程P1发送信号后,内核接受到信号,并将其放在P1的信号队列当中。当P1再次陷入内核态时,会检查信号队列,并根据相应的信号调取相应的信号处理函数。
信号检测和响应时机
刚才我们说,当P1再次陷入内核时,会检查信号队列。那么,P1什么时候会再次陷入内核呢?陷入内核后在什么时机会检测信号队列呢?
- 当前进程由于系统调用、中断或异常而进入系统空间以后,从系统空间返回到用户空间的前夕。
- 当前进程在内核中进入睡眠以后刚被唤醒的时候(必定是在系统调用中),或者由于不可忽略信号的存在而提前返回到用户空间。
进入信号处理函数
发现信号后,根据信号向量,知道了处理函数,那么该如何进入信号处理程序,又该如何返回呢?
我们知道,用户进程提供的信号处理函数是在用户态里的,而我们发现信号,找到信号处理函数的时刻处于内核态中,所以我们需要从内核态跑到用户态去执行信号处理程序,执行完毕后还要返回内核态。
- #include <signal.h>
- #include <stdio.h>
- void int_handler(int signum)
- {
- printf("\nSIGINT signal handler.\n");
- printf("exit.\n");
- exit(-1);
- }
- int main()
- {
- signal(SIGINT, int_handler);
- printf("int_handler set for SIGINT\n");
- while(1)
- {
- printf("go to sleep.\n");
- sleep(60);
- }
- return 0;
- }
信号量:
一.什么是信号量
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类
在学习信号量之前,我们必须先知道——Linux提供两种信号量:
(1) 内核信号量,由内核控制路径使用
(2)用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量。
1、POSIX信号量又分为有名信号量和无名信号量:
(1)有名信号量,其值保存在文件中,所以它既可以用于线程,也可以用于相关进程间,甚至是不相关进程。
(2)无名信号量,其值保存在内存中。无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。也就是说,无名信号量必须是多个进程(线程)的共享变量,无名信号量要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。
倘若对信号量没有以上的全面认识的话,你就会很快发现自己在信号量的森林里迷失了方向。
POSIX 信号量与SYSTEM V信号量的比较:
1. 对POSIX来说,信号量是个非负整数。常用于线程间同步。而SYSTEM
V信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为SYSTEM V IPC服务的,信号量只不过是它的一部分。常用于进程间同步。
2.POSIX信号量的引用头文件是“<semaphore.h>”,而SYSTEM V信号量的引用头文件是“<sys/sem.h>”。
3.从使用的角度,System V信号量是复杂的,而Posix信号量是简单。比如,POSIX信号量的创建和初始化或PV操作就很非常方便。
无名信号量:
- #include <pthread.h>
- #include <semaphore.h>
- #include <sys/types.h>
- #include <stdio.h>
- #include <unistd.h>
- int number; // 被保护的全局变量
- sem_t sem_id;
- void* thread_one_fun(void *arg)
- {
- sem_wait(&sem_id);
- printf("thread_one have the semaphore\n");
- number++;
- printf("number = %d\n",number);
- sem_post(&sem_id);
- }
- void* thread_two_fun(void *arg)
- {
- sem_wait(&sem_id);
- printf("thread_two have the semaphore \n");
- number--;
- printf("number = %d\n",number);
- sem_post(&sem_id);
- }
- int main(int argc,char *argv[])
- {
- number = 1;
- pthread_t id1, id2;
- sem_init(&sem_id, 0, 1);
- pthread_create(&id1,NULL,thread_one_fun, NULL);
- pthread_create(&id2,NULL,thread_two_fun, NULL);
- pthread_join(id1,NULL);
- pthread_join(id2,NULL);
- printf("main,,,\n");
- return 0;
- }
有名信号量:
- //File1: server.c </u>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <stdio.h>
- #include <semaphore.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #define SHMSZ 27
- char SEM_NAME[]= "vik";
- int main()
- {
- char ch;
- int shmid;
- key_t key;
- char *shm,*s;
- sem_t *mutex;
- //name the shared memory segment
- key = 1000;
- //create & initialize semaphore
- mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
- if(mutex == SEM_FAILED)
- {
- perror("unable to create semaphore");
- sem_unlink(SEM_NAME);
- exit(-1);
- }
- //create the shared memory segment with this key
- shmid = shmget(key,SHMSZ,IPC_CREAT|0666);
- if(shmid<0)
- {
- perror("failure in shmget");
- exit(-1);
- }
- //attach this segment to virtual memory
- shm = shmat(shmid,NULL,0);
- //start writing into memory
- s = shm;
- for(ch='A';ch<='Z';ch++)
- {
- sem_wait(mutex);
- *s++ = ch;
- sem_post(mutex);
- }
- //the below loop could be replaced by binary semaphore
- while(*shm != '*')
- {
- sleep(1);
- }
- sem_close(mutex);
- sem_unlink(SEM_NAME);
- shmctl(shmid, IPC_RMID, 0);
- exit(0);
- }
- //File 2: client.c</u>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <stdio.h>
- #include <semaphore.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #define SHMSZ 27
- char SEM_NAME[]= "vik";
- int main()
- {
- char ch;
- int shmid;
- key_t key;
- char *shm,*s;
- sem_t *mutex;
- //name the shared memory segment
- key = 1000;
- //create & initialize existing semaphore
- mutex = sem_open(SEM_NAME,0,0644,0);
- if(mutex == SEM_FAILED)
- {
- perror("reader:unable to execute semaphore");
- sem_close(mutex);
- exit(-1);
- }
- //create the shared memory segment with this key
- shmid = shmget(key,SHMSZ,0666);
- if(shmid<0)
- {
- perror("reader:failure in shmget");
- exit(-1);
- }
- //attach this segment to virtual memory
- shm = shmat(shmid,NULL,0);
- //start reading
- s = shm;
- for(s=shm;*s!=NULL;s++)
- {
- sem_wait(mutex);
- putchar(*s);
- sem_post(mutex);
- }
- //once done signal exiting of reader:This can be replaced by
- another semaphore
- *shm = '*';
- sem_close(mutex);
- shmctl(shmid, IPC_RMID, 0);
- exit(0);
- }
System V信号量:
- int sem_id = 0; /* semget的返回值,全局 */
- #define MUTEX 0 /* 用于返回临界区的信号量在集合中的序数 */
- #define NUM_SEM 1 /* 集合中信号量的个数 */
- #define SEM_KEY 0x11223344 /*保证内核中的唯一性 */
- void P(int sem_num)
- {
- struct sembuf sem;
- sem.sem_num = MUTEX;
- sem.sem_op = -1;
- sem.sem_flg = 0;
- if( -1 == semop(sem_id, &sem, 1) )
- {
- /* 错误处理 */
- }
- }
- void V(int sem_num)
- {
- struct sembuf sem;
- sem.sem_num = MUTEX;
- sem.sem_op = 1;
- sem.sem_flg = 0;
- if( -1 == semop(sem_id, &sem, 1) )
- {
- /* 错误处理 */
- }
- }
- 主函数:
- int main()
- {
- ...
- int semid;
- ....
- semid = semget(SEM_KEY, 0, 0); /* panduan 判断该型号量组是否已经存在 */
- if( -1 == semid )
- {
- semid = semget(SEM_KEY, NUM_SEM, IPC_CREAT | IPC_EXCL | 0666);
- if( -1 == semid)
- {
- /* 错误处理 */
- }
- else
- {
- semctl(sem_id, MUTEX, SETVAL, 1); /* 初始值为1。错误处理略 */
- }
- }
- ...
- P(MUTEX);
- /* 临界区代码段 */
- V(MUTEX);
- ...
- }
最全面的linux信号量解析:http://blog.csdn.net/qinxiongxu/article/details/7830537
信号只是一个数字,数字为0-31表示不同的信号,如下表所示。
编号 |
信号名 |
默认动作 |
说明 |
1 |
SIGHUP |
进程终止 |
终端断开连接 |
2 |
SIGINT |
进程终止 |
用户在键盘上按下CTRL+C |
3 |
SIGQUIT |
进程意外结束(Dump) |
用户在键盘上按下CTRL+\ |
4 |
SIGILL |
进程意外结束(Dump) |
遇到非法指令 |
5 |
SIGTRAP |
进程意外结束(Dump) |
遇到断电,用于调试 |
6 |
SIGABRT/SIGIOT |
进程意外结束(Dump) |
|
7 |
SIGBUS |
进程意外结束(Dump) |
总线错误 |
8 |
SIGFPE |
进程意外结束(Dump) |
浮点异常 |
9 |
SIGKILL |
进程终止 |
其他进程发送SIGKILL将导致目标进程终止 |
10 |
SIGUSR1 |
进程终止 |
应用程序可自定义使用 |
11 |
SIGSEGV |
进程意外结束(Dump) |
非法的内存访问 |
12 |
SIGUSR2 |
进程终止 |
应用程序可自定义使用 |
13 |
SIGPIPE |
进程终止 |
管道读取端已经关闭,写入端进程会收到该信号 |
14 |
SIGALRM |
进程终止 |
定时器到时 |
15 |
SIGTERM |
进程终止 |
发送该信号使目标进程终止 |
16 |
SIGSTKFLT |
进程终止 |
堆线错误 |
17 |
SIGCHLD |
忽略 |
子进程退出时会向父进程发送该信号 |
18 |
SIGCONT |
忽略 |
进程继续执行 |
19 |
SIGSTOP |
进程暂停 |
发送该信号会使目标进程进入TASK_STOPPED状态 |
20 |
SIGTSTP |
进程暂停 |
在终端上按下CTRL+Z |
21 |
SIGTTIN |
进程暂停 |
后台进程从控制终端读取数据 |
22 |
SIGTTOU |
进程暂停 |
后台进程从控制终端读取数据 |
23 |
SIGURG |
忽略 |
socket收到设置紧急指针标志的网络数据包 |
24 |
SIGXCPU |
进程意外结束(Dump) |
进程使用CPU已经超过限制 |
25 |
SIGXFSZ |
进程意外结束(Dump) |
进程使用CPU已经超过限制 |
26 |
SIGVTALRM |
进程终止 |
进程虚拟定时器到期 |
27 |
SIGPROF |
进程终止 |
进程Profile定时器到期 |
28 |
SIGMNCH |
忽略 |
进程终端窗口大小改变 |
29 |
SIGIO |
进程暂停 |
用于异步IO |
29 |
SIGPOLL |
进程暂停 |
用于异步IO |
30 |
SIGPWR |
进程暂停 |
电源失效 |
31 |
SIGUNUSED |
进程暂停 |
保留未使用 |
版权声明:本文为博主原创文章,未经博主允许不得转载。
Linux_信号与信号量【转】的更多相关文章
- Linux 进程通信之 ——信号和信号量总结
如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存. 所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一 ...
- Nginx之进程间的通信机制(信号、信号量、文件锁)
1. 信号 Nginx 在管理 master 进程和 worker 进程时大量使用了信号.Linux 定义的前 31 个信号是最常用的,Nginx 则通过重定义其中一些信号的处理方法来使用吸纳后,如接 ...
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...
- Linux 信号量详解一
信号量主要用于进程间(不是线程)的互斥,通过sem_p()函数加锁使用资源,sem_v函数解锁释放资源,在加锁期间,CPU从硬件级别关闭中断,防止pv操作被打断. semget函数 int semge ...
- Linux进程间通信(五):信号量 semget()、semop()、semctl()
这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信 -- 信号.下面 ...
- [No00003C]操作系统Operating Systems进程同步与信号量Processes Synchronization and Semaphore
操作系统Operating Systems进程同步与信号量Processes Synchronization and Semaphore 进程合作:多进程共同完成一个任务 从纸上到实际:生产者− − ...
- linux第11天 共享内存和信号量
今天主要学习了共享内存和信号量 在此之前,有个管道问题 ls | grep a 整句话的意思是将ls输出到管道的写端,而流通到另一端的读端,grep a则是从管道的读端读取相关数据,再做筛选 共享内存 ...
- IPC——信号量
Linux进程间通信——使用信号量 这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:L ...
- linux进程间通讯-System V IPC 信号量
进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...
随机推荐
- 【poj2096】Collecting Bugs 期望dp
题目描述 Ivan is fond of collecting. Unlike other people who collect post stamps, coins or other materia ...
- P1825 [USACO11OPEN]玉米田迷宫Corn Maze
题目描述 This past fall, Farmer John took the cows to visit a corn maze. But this wasn't just any corn m ...
- 【BZOJ1486】最小圈(分数规划)
[BZOJ1486]最小圈(分数规划) 题面 BZOJ 洛谷 求图中边权和除以点数最小的环 题解 分数规划 二分答案之后将边权修改为边权减去二分值 检查有无负环即可 #include<iostr ...
- BZOJ1901:Zju2112 Dynamic Rankings——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1901 Description 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序 ...
- Vue_WebPack小白入门
Vue语法笔记 Vue项目搭建过程 Vue问题总结 去掉vue 中的代码规范检测(Eslint验证) 解决跨域请求问题 Vue推荐资料
- sass的颜色函数
sass中有些非常实用的颜色处理函数,总结如下 1.颜色加深或变浅 lighten($color,$amount) //颜色变浅 darken($color,$amount) //颜色加深 例如: l ...
- [BZOJ1131/POI2008]Sta树的深度
Description 给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大 Input 给出一个数字N,代表有N个点.N<=1000000 下面N-1条边. Output ...
- Android数据存储与访问
1.文件 1)保存到手要内存,文件保存到/data/data对应的应用程序包下面 如 FILE_PATH = "/data/data/com.diysoul.filedem ...
- C++ 什么是多态
一.什么是多态(Polymorphism) 多态(Polymorphism)是面向对象(Object-Oriented,OO)思想"三大特征"之一,其余两个分别是封装(Encaps ...
- 第01篇 说一下Setting,我一直没有讲过
settings 调整 settings 中的设置是非常关键的,它们会改变 MyBatis 的运行时行为.下表描述了设置中各项的意图.默认值等. 设置参数 描述 有效值 默认值 cacheEn ...