Linux_信号与信号量
Posted 逝去的浪花
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux_信号与信号量相关的知识,希望对你有一定的参考价值。
信号:
信号机制是类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,说明 它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类
(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_进程信号(进程信号生命周期_Core Dump调试_进程信号捕捉_系统调用向进程发送信号_阻塞信号_信号集函数_处理信号内核态与用户态_C语言volatile关键字_SIGCHLD信号)(