进程间通信之信号he信号量

来源:互联网 发布:有乎科技 编辑:程序博客网 时间:2024/05/29 02:40

信号的篇幅较少,就把他和信号量放在一起了。先讲讲他们之间的区别:
1.信号:(signal)是一种处理异步事件的方式。信号时比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程外,还可以发送信号给进程本身。linux除了支持unix早期的信号语义函数,还支持语义符合posix.1标准的信号函数sigaction。

2.信号量:(Semaphore)进程间通信处理同步互斥的机制。是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

信号:
使用信号要先知道有哪些信号,在Linux下有31个需要记住的通用信号,篇幅限制,这里就不赘述了。
signal:

#include <stdio.h>#include <signal.h>// 信号处理函数void handle(int signum){    printf ("捕捉到一个信号 %d\n", signum);}int main(){    signal(SIGINT, handle);    signal(SIGTERM, handle);    while (1);    return 0;}

kill:

#include <stdio.h>#include <signal.h>int main(){    //while (1)    {        kill (pid, sig);        sleep(1);    }    return 0;}

alarm:

#include <stdio.h>#include <signal.h>// 信号处理函数void handle(int signum){    printf ("hello world\n");    // 定时器重置    alarm(2);}int main(){    // 定时器是一次性的    alarm(2);    signal(SIGALRM, handle);    while (1);    return 0;}

sigaction:

#include <stdio.h>#include <signal.h>// 信号处理函数void handle(int signum){    printf ("hello world\n");    // 定时器重置    alarm(2);}int main(){    // 定时器是一次性的    alarm(2);    struct sigaction act;    act.sa_handler = handle;    sigaction(SIGALRM, &act, NULL);    while (1);    return 0;}

处理子进程的退出:

#include <stdio.h>#include <signal.h>#include <sys/types.h>#include <sys/wait.h>// 信号处理函数void handle(int signum){    while (waitpid (-1, NULL, WNOHANG) > 0)    {        printf ("捕获一个子进程\n");    }    printf ("111111\n");}int main(){    signal (SIGCHLD, handle);    int count = 10;    while (count--)    {        pid_t pid = fork();         switch (pid)        {            case -1:                perror ("fork");                break;            case 0: // 子进程                printf ("我是子进程,我的Id 是%d\n", getpid());                sleep(1);                exit(0);            default: // 父进程                printf ("我是父进程,Id = %d\n", getpid());                break;        }    }    while (1);    return 0;}

信号量

一、什么是信号量

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

二、信号量的工作原理

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

三、Linux的信号量机制

Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。
1、semget函数
它的作用是创建一个新信号量或取得一个已有信号量,原型为:

int semget(key_t key, int num_sems, int sem_flags);

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

semget函数成功返回一个相应信号标识符(非零),失败返回-1.

2、semop函数
它的作用是改变信号量的值,原型为:

int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);  

sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:

struct sembuf{      short sem_num;//除非使用一组信号量,否则它为0      short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,                      //一个是+1,即V(发送信号)操作。      short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,                      //并在进程没有释放该信号量而终止时,操作系统释放信号量  };  

3、semctl函数
该函数用来直接控制信号量信息,它的原型为:

int semctl(int sem_id, int sem_num, int command, ...);  

如果有第四个参数,它通常是一个union semum结构,定义如下:

union semun{      int val;      struct semid_ds *buf;      unsigned short *arry;  };  

前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

原创粉丝点击