信号:
信号机制是类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 |
进程暂停 |
保留未使用 |