Linux_信号与信号量【转】

简介: 转自:http://blog.csdn.net/sty23122555/article/details/51470949 信号: 信号机制是类UNIX系统中的一种重要的进程间通信手段之一。我们经常使用信号来向一个进程发送一个简短的消息。

转自: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什么时候会再次陷入内核呢?陷入内核后在什么时机会检测信号队列呢?

  1. 当前进程由于系统调用、中断或异常而进入系统空间以后,从系统空间返回到用户空间的前夕。
  2. 当前进程在内核中进入睡眠以后刚被唤醒的时候(必定是在系统调用中),或者由于不可忽略信号的存在而提前返回到用户空间。

进入信号处理函数

发现信号后,根据信号向量,知道了处理函数,那么该如何进入信号处理程序,又该如何返回呢?

我们知道,用户进程提供的信号处理函数是在用户态里的,而我们发现信号,找到信号处理函数的时刻处于内核态中,所以我们需要从内核态跑到用户态去执行信号处理程序,执行完毕后还要返回内核态。


 

  1. #include <signal.h>  
  2. #include <stdio.h>  
  3.   
  4. void int_handler(int signum)  
  5. {  
  6.  printf("\nSIGINT signal handler.\n");  
  7.  printf("exit.\n");  
  8.  exit(-1);  
  9. }  
  10.   
  11. int main()  
  12. {  
  13.  signal(SIGINT, int_handler);  
  14.  printf("int_handler set for SIGINT\n");  
  15.   
  16.   while(1)  
  17.  {  
  18.   printf("go to sleep.\n");  
  19.   sleep(60);  
  20.  }  
  21.   
  22.   return 0;  
  23. }  


信号量:

一.什么是信号量
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为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操作就很非常方便。

无名信号量:

 

  1. #include <pthread.h>  
  2. #include <semaphore.h>  
  3. #include <sys/types.h>  
  4. #include <stdio.h>  
  5. #include <unistd.h>  
  6. int number; // 被保护的全局变量  
  7. sem_t sem_id;  
  8. void* thread_one_fun(void *arg)  
  9. {  
  10. sem_wait(&sem_id);  
  11. printf("thread_one have the semaphore\n");  
  12. number++;  
  13. printf("number = %d\n",number);  
  14. sem_post(&sem_id);  
  15. }  
  16. void* thread_two_fun(void *arg)  
  17. {  
  18. sem_wait(&sem_id);  
  19. printf("thread_two have the semaphore \n");  
  20. number--;  
  21. printf("number = %d\n",number);  
  22. sem_post(&sem_id);  
  23. }  
  24. int main(int argc,char *argv[])  
  25. {  
  26. number = 1;  
  27. pthread_t id1, id2;  
  28. sem_init(&sem_id, 0, 1);  
  29. pthread_create(&id1,NULL,thread_one_fun, NULL);  
  30. pthread_create(&id2,NULL,thread_two_fun, NULL);  
  31. pthread_join(id1,NULL);  
  32. pthread_join(id2,NULL);  
  33. printf("main,,,\n");  
  34. return 0;  
  35. }  

有名信号量:
  1. //File1: server.c </u>  
  2. #include <sys/types.h>  
  3. #include <sys/ipc.h>  
  4. #include <sys/shm.h>  
  5. #include <stdio.h>  
  6. #include <semaphore.h>  
  7. #include <sys/types.h>  
  8. #include <sys/stat.h>  
  9. #include <fcntl.h>  
  10. #define SHMSZ 27  
  11. char SEM_NAME[]= "vik";  
  12. int main()  
  13. {  
  14. char ch;  
  15. int shmid;  
  16. key_t key;  
  17. char *shm,*s;  
  18. sem_t *mutex;  
  19. //name the shared memory segment  
  20. key = 1000;  
  21. //create & initialize semaphore  
  22. mutex = sem_open(SEM_NAME,O_CREAT,0644,1);  
  23. if(mutex == SEM_FAILED)  
  24. {  
  25. perror("unable to create semaphore");  
  26. sem_unlink(SEM_NAME);  
  27. exit(-1);  
  28. }  
  29. //create the shared memory segment with this key  
  30. shmid = shmget(key,SHMSZ,IPC_CREAT|0666);  
  31. if(shmid<0)  
  32. {  
  33. perror("failure in shmget");  
  34. exit(-1);  
  35. }  
  36. //attach this segment to virtual memory  
  37. shm = shmat(shmid,NULL,0);  
  38. //start writing into memory  
  39. s = shm;  
  40. for(ch='A';ch<='Z';ch++)  
  41. {  
  42. sem_wait(mutex);  
  43. *s++ = ch;  
  44. sem_post(mutex);  
  45. }  
  46. //the below loop could be replaced by binary semaphore  
  47. while(*shm != '*')  
  48. {  
  49. sleep(1);  
  50. }  
  51. sem_close(mutex);  
  52. sem_unlink(SEM_NAME);  
  53. shmctl(shmid, IPC_RMID, 0);  
  54. exit(0);  
  55. }  

  1. //File 2: client.c</u>  
  2. #include <sys/types.h>  
  3. #include <sys/ipc.h>  
  4. #include <sys/shm.h>  
  5. #include <stdio.h>  
  6. #include <semaphore.h>  
  7. #include <sys/types.h>  
  8. #include <sys/stat.h>  
  9. #include <fcntl.h>  
  10. #define SHMSZ 27  
  11. char SEM_NAME[]= "vik";  
  12. int main()  
  13. {  
  14. char ch;  
  15. int shmid;  
  16. key_t key;  
  17. char *shm,*s;  
  18. sem_t *mutex;  
  19. //name the shared memory segment  
  20. key = 1000;  
  21. //create & initialize existing semaphore  
  22. mutex = sem_open(SEM_NAME,0,0644,0);  
  23. if(mutex == SEM_FAILED)  
  24. {  
  25. perror("reader:unable to execute semaphore");  
  26. sem_close(mutex);  
  27. exit(-1);  
  28. }  
  29. //create the shared memory segment with this key  
  30. shmid = shmget(key,SHMSZ,0666);  
  31. if(shmid<0)  
  32. {  
  33. perror("reader:failure in shmget");  
  34. exit(-1);  
  35. }  
  36. //attach this segment to virtual memory  
  37. shm = shmat(shmid,NULL,0);  
  38. //start reading  
  39. s = shm;  
  40. for(s=shm;*s!=NULL;s++)  
  41. {  
  42. sem_wait(mutex);  
  43. putchar(*s);  
  44. sem_post(mutex);  
  45. }  
  46. //once done signal exiting of reader:This can be replaced by  
  47. another semaphore  
  48. *shm = '*';  
  49. sem_close(mutex);  
  50. shmctl(shmid, IPC_RMID, 0);  
  51. exit(0);  
  52. }  


System V信号量:

 

  1. int sem_id = 0; /* semget的返回值,全局 */  
  2. #define MUTEX 0 /* 用于返回临界区的信号量在集合中的序数 */  
  3. #define NUM_SEM 1 /* 集合中信号量的个数 */  
  4. #define SEM_KEY 0x11223344 /*保证内核中的唯一性 */  
  5.   
  6. void P(int sem_num)  
  7. {  
  8.     struct sembuf sem;  
  9.     sem.sem_num = MUTEX;  
  10.     sem.sem_op = -1;  
  11.     sem.sem_flg = 0;  
  12.        
  13.     if( -1 == semop(sem_id, &sem, 1) )  
  14.     {  
  15.          /* 错误处理 */  
  16.     }  
  17. }  
  18.   
  19.   
  20. void V(int sem_num)  
  21. {  
  22.     struct sembuf sem;  
  23.     sem.sem_num = MUTEX;  
  24.     sem.sem_op = 1;  
  25.     sem.sem_flg = 0;  
  26.        
  27.     if( -1 == semop(sem_id, &sem, 1) )  
  28.     {  
  29.          /* 错误处理 */  
  30.     }  
  31. }  
  32.   
  33. 主函数:  
  34.   
  35. int main()  
  36. {  
  37.     ...  
  38.     int semid;  
  39.     ....  
  40.     semid = semget(SEM_KEY, 0, 0); /* panduan 判断该型号量组是否已经存在 */  
  41.     if( -1 == semid )  
  42.     {  
  43.         semid = semget(SEM_KEY, NUM_SEM, IPC_CREAT | IPC_EXCL | 0666);  
  44.         if( -1 == semid)  
  45.         {  
  46.             /* 错误处理 */  
  47.         }  
  48.         else   
  49.         {  
  50.            semctl(sem_id, MUTEX, SETVAL, 1); /* 初始值为1。错误处理略 */  
  51.         }  
  52.     }  
  53.   
  54.     ...  
  55.     P(MUTEX);  
  56.     /* 临界区代码段 */  
  57.     V(MUTEX);  
  58.     ...  
  59. }  


最全面的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

  进程暂停

  保留未使用



版权声明:本文为博主原创文章,未经博主允许不得转载。

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
Linux
22、linux信号学习(1)
1、最简单的安装信号 示例1 View Code #include using namespace std;#include void func(int sig){ cout0 将信号传给进程识别码为pid 的进程。
733 0
|
Web App开发 Linux
23、linux信号学习(2)
5)sigaddset #include int sigaddset(sigset_t *set,int signum); ① sigaddset()用来将参数signum 代表的信号加入至参数set 信号集里。
694 0
|
4天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
14天前
|
NoSQL Linux Shell
常用的 Linux 命令
常用的 Linux 命令
36 9
|
23小时前
|
监控 Linux Windows
50个必知的Linux命令技巧,你都掌握了吗?(下)
50个必知的Linux命令技巧,你都掌握了吗?(下)
|
1天前
|
Linux Shell Windows
Linux 常用基本命令
Linux 常用基本命令
|
2天前
|
Ubuntu Linux Shell
linux免交互登陆远程主机并执行命令(密钥对和Expect)
linux免交互登陆远程主机并执行命令(密钥对和Expect)
|
2天前
|
Linux
【Linux】常用命令
【Linux】常用命令
21 0
|
2天前
|
安全 Ubuntu Linux
Linux 网络操作命令Telnet
Linux 网络操作命令Telnet
14 0
Linux 网络操作命令Telnet
|
3天前
|
Linux 数据安全/隐私保护
Linux常用命令实例带注释
Linux常用命令实例带注释
30 0