进击吧,linux(十五) 信号量互斥编程
0赞
Linux进程和进程之间有多种通讯方式。Linux进程间通讯的主要方式有:
1、 无名管道
2、 有名管道
3、 信号
4、 消息队列
5、 共享内存
6、 信号量
7、 套接字
信号通讯包括有信号量互斥。信号量可以认为是一个资源,当一个进程使用该信号量后,如果信号量互斥的话,那么其他进程就不能使用该信号量,要直到等待使用该信号量的进程结束后,才能使用该信号量。
信号量编程主要是有三个函数。
8.1.1 函数名
semget
8.1.2 函数原形
int semget(key_t key, int nsems, int semflg)
8.1.3 函数功能
获取一个信号量的集合标识符,当key指定的信号量不存在的时候并且semflg包含了IPC_CREAT,就会创建一个信号量集合。
8.1.4 所属头文件
<sys/types.h> <sys/ipc.h> <sys/sem.h>
8.1.5 返回值
成功: 信号量集合标识符
失败: -1
8.1.6 参数说明
key: 打开的信号量的键值,使用ftok(char *,int )创建的键值
nsems:指定信号量集合包含的信号量个数
semflg:标志
-IPC_CREAT:KEY所关联的信号量不存在,就创建信号量
8.2.1 函数名
semop
8.2.2 函数原形
int semop(int semid, struct sembuf *sops, unsigned nsops)
8.2.3 函数功能
对信号量操作,申请或者释放信号量
8.2.4 所属头文件
<sys/types.h> <sys/ipc.h> <sys/sem.h>
8.2.5 返回值
成功: 0
失败: -1
8.2.6 参数说明
semid: 要操作的信号量集合的标识符
sops: 信号量操作的数组,每一个数组元素对应信号量的操作
struct sembuf{
unsigned short sem_num; /*semaphore number*/
short sem_op; /*semaphore operation*/
short sem_flg; /*operation flags*/
}
sem_num: 对信号量集合里面的哪些信号操作
sem_op: 信号量的操作,正数表示释放信号量,负数表示请求信号量,如果没有请求到信号量,那么进程就阻塞,等到有信号量为止。
sem_flg:
-SEM_UNDO:当信号操作失败,系统会自动的释放掉信号量
-SEM_NOWAIT:当信号操作造成进程阻塞,不阻塞程序,继续执行
nsops: 要操作多少个信号量
8.3 控制信号量
8.3.1 函数名
semctl
8.3.2 函数原型
int semctl(int semid, int semnum, int cmd, …)
8.3.3 函数功能
由cmd决定对信号量集合的操作
8.3.4所属头文件
<sys/types.h> <sys/ipc.h> <sys/sem.h>
8.3.5返回值
失败:-1
成功:根据不同的命令返回不同的值
GETVAL: 返回信号量的值
8.3.6 参数说明
semid:要操作的信号量集合的标识符
semnum: 命令的参数
可以看出,这个参数就是一个联合体。4个字节大小。根据不同的命令,填入不同的值。
cmd: 命令
SETVAL: 设置信号量的值
GETVAL: 获取信号量的值
…: 可选参数,根据命令,有不同的参数
在信号量中,有一个很重要的东西,就是键值。Linux将信号量集合和一个键值给关联起来,键值其实就是一个整数。当键值确定了,那么对应的信号量集合就知道了,就可以打开、创建或者操作信号量集合了。一个信号量集合可以包含多个信号量。
对于这个键值,有两种方法来指定:
1、 任意指定一个数
如果这个数已经被其他的IPC (Internet Process Connection)所使用,那么就会在关联信号量集合时失败,所以这种方法不推荐
2、使用key_t ftok( char * fname, int id )来创建一个键值
参数第一个是一个目录的路径,第二个是一个任意的数字
以下就写程序来学习一下怎么使用信号量互斥。这里,假设有两个进程AB都想向屏幕上打印数据,但是B进程必须要等待A进程打印完后才能打印。
首先是A进程
#include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> #include<stdio.h> void main() { key_t key; int semid; int n; struct sembuf sops[2]; /*创建键值*/ key = ftok("/home",3); /*创建并打开信号量*/ semid = semget(key,2,IPC_CREAT); /*设置信号量*/ semctl(semid,0,SETVAL,1); semctl(semid,1,SETVAL,1); /*读取信号量的值*/ n = semctl(semid,0,GETVAL); printf("%d\n",n); n = semctl(semid,1,GETVAL); printf("%d\n",n); /*获取信号量*/ sops[0].sem_num = 0; sops[0].sem_op = -1; sops[0].sem_flg = SEM_UNDO; sops[1].sem_num = 1; sops[1].sem_op = -1; sops[1].sem_flg = SEM_UNDO; semop(semid,&sops[0],2); /*打印*/ printf("hello "); /*暂停,休眠8秒钟*/ sleep(4); /*打印*/ printf("weiqi7777\n"); /*释放信号量*/ sops[0].sem_num = 0; sops[0].sem_op = 1; sops[0].sem_flg = SEM_UNDO; sops[1].sem_num = 1; sops[1].sem_op = 1; sops[1].sem_flg = SEM_UNDO; semop(semid,&sops[0],2); }
在程序中,首先使用ftok创建了一个键值。使用的第一个参数是/home目录,第二个是随意一个数字。
然后创建一个信号量集合。信号量集合包含了2个信号量。信号量分别对应struct sembuf sops[0]和struct sembuf sops[1]。
semctl,是一个控制信号量函数,这里是设置信号量集合中信号量的值,因为信号量集合是包括2个信号量的。所以就是依次设定信号量1和信号量2的值都是1。然后再对信号量的值读取,打印出来。对于信号量互斥,信号量的值要设置为1,表示同一时刻只有一个进程可以申请到资源。
然后是对信号量进行获取,其实就是填写结构体,因为是对2个信号量的操作,所以要填写两个结构体的值。
然后调用semop函数,进行信号量获取。这里要注意,第二个参数要用&sops[0]或者是sops,不能是&sops。这个是牵涉到C语言指针的知识。
打印数据,最后释放信号量。
然后是B进程:
#include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> void main() { key_t key; int semid; int n; struct sembuf sops[2]; /*创建键值*/ key = ftok("/home",3); /*打开信号量*/ semid = semget(key,2,IPC_CREAT); /*读取信号量值*/ n = semctl(semid,0,GETVAL); printf("%d\n",n); n = semctl(semid,1,GETVAL); printf("%d\n",n); /*获取信号量*/ sops[0].sem_num = 0; sops[0].sem_op = -1; sops[0].sem_flg = SEM_UNDO; sops[1].sem_num = 1; sops[1].sem_op = -1; sops[1].sem_flg = SEM_UNDO; semop(semid,&sops[0],2); /*打印*/ printf("http://blog.chinaaet.com/weiqi7777\n"); /*释放信号量*/ sops[0].sem_num = 0; sops[0].sem_op = 1; sops[0].sem_flg = SEM_UNDO; sops[1].sem_num = 1; sops[1].sem_op = 1; sops[1].sem_flg = SEM_UNDO; semop(semid,&sops[0],2); }
对于B进程,要注意的是,创建的键值要和A进程创建的进程一致,这样,两个进程才会关联到同一个信号量集合,才能对信号量集合进行一致操作。
B进程获取信号量,因为A进程已经获取信号量了,所以B进程就会获取失败,然后B进程就会被阻塞掉。直到能获取到信号量为止。当A进程释放信号量后,B进程就获取到信号量了,然后执行自己的程序,最后释放信号量。
程序执行效果:
A进程首先执行,创建信号量并设置信号量,然后获取信号量。B进程再执行,但是获取信号量失败,所以B进程就阻塞。
当A进程释放信号量后,B进程就会获取到信号量,然后B进程继续执行。这样,就实现了信号量的互斥操作。
互斥操作,主要协调多个进程对同一个资源的使用。比如对于串口,只有1个,有多个进程都要访问串口,那肯定不能大家同时去操作串口,所以就可以用信号量同步,给串口设置一个互斥信号量,只有获取到这个信号量的进程,才能对串口进行操作。这样,就防止了多个进程同时对一个资源进行控制了。