信号量我就不介绍了,因为这个之前学习操作系统的时候已经介绍过了
我们引入信号量的主要目的就是为了解决共享资源在访问时的同步问题
主要就是PV
操作,这两个字母均来自于希腊文字passeren
和vrijgeven
LINUX提供了一套操作信号量的API
,都定义在头文件sys/sem.h
中
信号量的创建
我们可以使用函数semget
来创建信号量,原型如下:(sem
是信号量semaphore
的缩写)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
和之前的消息队列的创建函数都是一个德行,存在则访问,否则创建
参数nsems
指定创建的信号量的个数
参数semflg
的低9
位确定了信号量属组以及其他用户的访问权限,还有一些位指定了信号量的创建方式,其取值与含义如下所示:
参数 | 描述 |
---|---|
IPC_CREAT | 创建信号量,如果信号量已经存在,就打开该信号量 |
IPC_EXCL | 与宏IPC_CREAT一起使用,单独使用无意义,此时只能创建一个内核中不存在的信号量,如果要创建的信号量已经存在,则会创建失败 |
semget
函数的一个例子:
int semid;
semid = semget(0x1234, 10, 0666|IPC_CREAT);
信号量的修改
函数:semop
函数名起的很同意理解semaphore operate
int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops);
sem_id
就是我们上面使用semget
函数的返回值
sem_opa
的类型是一个结构体sembuf
struct sembuf{
short sem_num; //除非使用一组信号量,否则它为0
short sem_op; //信号量在一次操作中需要改变的数据,通常是两个数
//一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号,
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
补充一下结构体中sem_op
的描述:
- sem_op的值为正数
- 该值会加到现有的信号内含值中,通常用于释放所控资源的使用权
- sem_op的值为负数
- 而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值,通常用于获取资源的使用权
- sem_op的值为0
- 如果没有设置IPC_NOWAIT,则调用该操作的进程或者线程将暂时睡眠,直到信号量的值为0
- 否则
- 进程或者线程不会睡眠,函数返回错误EAGAIN**
信号量的初始化和删除
函数:semctl
–>semaphore control
int semctl(int sem_id,int sem_num,int command,[union semun sem_union]);
参数sem_num
是信号量在信号量集合中的编号,第一个信号的编号是0
参数command
有两个取值:
SETVAL
- 初始化信号量
IPC_RMID
- 删除信号量
参数sem_union
可选,是一个联合,结构如下:
union semun{
int val; //一般用到的是val,表示要传给信号量的初始值
struct semid_ds *buf;
unsigned short *arry;
};
这里顺便复习一波union和struct的区别
-
union
中的成员的内存是共享的,这个理解起来也很容易,我们来看一段代码:union myun { struct { int x; int y; int z; }u; int k; }a; int main() { a.u.x =4; a.u.y =5; a.u.z =6; a.k = 0; printf("%d %d %d\n",a.u.x,a.u.y,a.u.z); return 0; }
我们在
union
中嵌入了一个struct
,都是int
类型,每个变量各占四个字节,根据union
中成员共享内存的特性,他们在内存中的排列列顺序应该是这样的当我们最后给
k
赋值为0
的时候,相应的,x
的值就被覆盖了,因此最后的输出应该是0 5 6
union
主要适用于这样的场景-
由多个对象、多个事物,但是我们只需要其中一个,在实际中的应用可能是下面这样的:(报文格式的设计)
struct CommuPacket { int iPacketType; //报文类型标志 union //每次传送的是三种报文中的一种,使用union { struct structA packetA; struct structB packetB; struct structC packetC; } };
-
下面贴一个从网上找到的信号量
函数的使用示例:
include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_id;
int set_semvalue()
{
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id,0,SETVAL,sem_union)==-1)
return 0;
return 1;
}
int semaphore_p()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1)==-1)
{
fprintf(stderr,"semaphore_p failed\n");
return 0;
}
return 1;
}
int semaphore_v()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1)==-1)
{
fprintf(stderr,"semaphore_v failed\n");
return 0;
}
return 1;
}
void del_semvalue()
{
//删除信号量
union semun sem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union)==-1)
fprintf(stderr,"Failed to delete semaphore\n");
}
int main(int argc,char *argv[])
{
char message = 'x';
//创建信号量
sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
if(argc>1)
{
//初始化信号量
if(!set_semvalue())
{
fprintf(stderr,"init failed\n");
exit(EXIT_FAILURE);
}
//参数的第一个字符赋给message
message = argv[1][0];
}
int i=0;
for(i=0;i<5;i++)
{
//等待信号量
if(!semaphore_p())
exit(EXIT_FAILURE);
printf("%c",message);
fflush(stdout);
sleep(1);
//发送信号量
if(!semaphore_v())
exit(EXIT_FAILURE);
sleep(1);
}
printf("\n%d-finished\n",getpid());
if(argc>1)
{
//退出前删除信号量
del_semvalue();
}
exit(EXIT_SUCCESS);
}
运行结果:
root@ubuntu:~# ./test.exe 0 & ./test.exe
[1] 20393
x0x0x0x0x0
20394-finished
root@ubuntu:~#
20393-finished
信号量的存在使得两个进程交替打印字符