Linux_信号与信号量

信号:

信号机制是类UNIX系统中的一种重要的进程间通信手段之一。我们经常使用信号来向一个进程发送一个简短的消息。例如:假设我们启动一个进程通过socket读取远程主机发送过来的网络数据包,此时由于网络因素当前主机还没有收到相应的数据,当前进程被设置为可中断等待状态(TASK_INTERRUPTIBLE),此时我们已经失去耐心,想提前结束这个进程,于是可以通过kill命令想这个进程发送KILL信号,内核会唤醒该进程,执行它的信号处理函数,KILL信号的默认处理是退出该进程。

另外应用程序可以通过signal()等函数来为一个信号设置默认处理函数。例如当用户按下CTRL+C时,shell将会发出SIGINT信号,SIGINT的默认处理函数是执行进程的退出代码,如下所示:

可以通过类似下面的命令显式的给一个进程发送一个信号:

kill -2 pid

事实上,进程也不知道信号到底什么时候到达。信号是异步的,一个进程不可能等待信号的到来,也不知道信号会到来,那么,进程是如何发现和接受信号呢?实际上,信号的接收不是由用户进程来完成的,而是由内核代理。当一个进程P2向另一个进程P1发送信号后,内核接受到信号,并将其放在P1的信号队列当中。当P1再次陷入内核态时,会检查信号队列,并根据相应的信号调取相应的信号处理函数。

信号检测和响应时机

刚才我们说,当P1再次陷入内核时,会检查信号队列。那么,P1什么时候会再次陷入内核呢?陷入内核后在什么时机会检测信号队列呢?

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

进入信号处理函数

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

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

#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


  进程暂停


  保留未使用

 

时间: 2024-10-29 19:12:00

Linux_信号与信号量的相关文章

Linux 进程通信之 ——信号和信号量总结

如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一样.通信是一个广义上的意义,不仅仅指传递一些massege.他们的用法是基本相同的,所以仅仅要掌握了一种的用法,然后记住其他的用法就能够了. 1. 信号       在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也能够用来做其他的事      情,可是我还不知道做什么.       信号和信号量是

进程-IPC 信号和信号量 (二)

详细见:  https://github.com/ZhangzheBJUT/linux/blob/master/IPC(%E4%BA%8C).md 三 信号 3.1. 信号简介 信号是进程之间互相传递消息的一种方法,信号全称为软中断信号,也可称为软中断,它是实现IPC的方法之一. 信号是UNIX和Linux系统响应某些条件而产生的一个事件.进程之间可以互相通过系统调用kill发送软中断信号.内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件. 注:信号只是简单用来通知某进程发生了什么

Nginx之进程间的通信机制(信号、信号量、文件锁)

1. 信号 Nginx 在管理 master 进程和 worker 进程时大量使用了信号.Linux 定义的前 31 个信号是最常用的,Nginx 则通过重定义其中一些信号的处理方法来使用吸纳后,如接收到 SIGUSR1 信号就意味着需要重新打开文件. 使用信号时 Nginx 定义了一个 ngx_signal_t 结构体用于描述接收到的信号时的行为: typedef struct { // 需要处理的信号 int signo; // 信号对应的字符串名称 char *signame; // 这个

信号,信号量,锁,条件变量,消息通信,共享内存,RPC (一)

在实际项目当中,经常需要把一个功能分成多个子模块实现.那么,这些子模块之间该如何关联起来呢?静态地看,模块可以看作一组完成相同功能的函数:而动态地看,模块可以是一个独立的进程.线程或者一个中断服务或者信号服务例程.根据不同的具体业务实现,它们之间可能是静态调用.动态互斥.同步.唤醒等关系.静态的调用很好实现,上层的函数调用底层的函数即可.那么,动态互斥.同步.唤醒等关系,又该如何实现呢?这就设计到我们将要讨论的信号.进程间消息通信.共享内存.线程互斥同步条件变量.RPC等手段.下面就按照Linu

Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> 参考:C和指针学习 说明:本文非常的长,也是为了便于查找和比较,所以放在一起了 Linux 传统的进程间通信有很多,如各类管道.消息队列.内存共享.信号量等等.但它们都无法介于内核态与用户态使用,原因如表 通信方法 无法介于内核态与用户态的原因 管道(不包括命名管道) 局限于父子进程间的通信. 消息队列 在

进程同步和信号量

进程的同步 进城合作:多进程完成同一个任务 ? 实例1: 司机 ? ? ? ? ? ? ? ? ? ? ? ? ?售票员 while)true){ ? ? ? ? ? ? ? ? ?while)true){ 等待 启动车辆; ? ? ? ? ? ? ? ? ? ?关门; 发送信号 ? ? 正常运行: ? ? ? ? ? ? ? ? ? ?售票; ? ? 到站停车:发送信号 ? ? ? ? ? ?等待 开门; } ? ? ? ? ? ? ? ? ? ? ? ? ? ? } 两个进程如何实现同步合作

linux进程间通讯-System V IPC 信号量

进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信--使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在

Linux 下信号理解(一)

Linux提供了信号传递进程消息的机制,什么是信号?它是一种非常短的消息,短到只有一个数字.值得强调的是信号和信号量只少了一个字,但他们完全是不同的概念,信号量仅用于同步代码段,而信号则用于传递消息. 一 .信号的编号:通过kill -l 命令可以看到 二.信号机制 可以通过man 7 signal 三.几种默认处理信号的方式: Term表示终止当前进程. Core表示终止当前进程并且Core Dump 生成core文件用于调试(Core Dump 用于gdb调试). Ign表示忽略该信号. S

Linux IPC实践(11) --System V信号量(1)

信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); int semctl(int semid, int semnum, int cmd, ...); int semop(int semid, struct sembuf *sops, unsigned nsops); semget int