1、ftok函数
#include <sys/ipc.h>
#include <sys/types.h>
key_t ftok(const char* path, int id);
ftok 函数把一个已存在的路径名和一个整数标识转换成一个key_t值,即IPC关键字
path 参数就是你指定的文件名(已经存在的文件名),一般使用当前目录。当产生键时,只使用id参数的低8位。
id 是子序号, 只使用8bit (1-255)
返回值:若成功返回键值,若出错返回(key_t)-1
在一般的UNIX实现中,是将文件的索引节点号取出(inode),前面加上子序号的到key_t的返回值
2、semget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget( key_t key, int nsems, int semflg);
用来创建一个信号集,或者获取已存在的信号集。
key: 所创建或打开信号量集的键值(ftok成果执行的返回值)。
nsems:创建的信号量集中的信号量个数,该参数只在创建信号量时有效。
semflg :调用函数的操作类型,也可用于设置信号量集的访问权限,通过or运算使用。
IPC_CREAT | IPC _EXCL | 0666 :一般用于创建,可保证返回一个新的ID,同时制定权限为666
IPC_CREAT : 用于获取一个已经存在的ID
返回值:成功返回信号量集的标识符,失败返回-1,errno被设置成以下的某个值:
EACESS : 没有访问该信号量集的权限。
EEXIST:信号量集已经存在,无法创建。
EINVAL:参数nsems的值小于0,或者大于该信号量集的限制,或者是该key关联的信号量以存在,并且nsems的值大于该信号量集的信号量数。
ENOENT:信号量集不存在,同时没有使用,IPC_CREAT。
ENOMEM:没有足够的内存创建新的信号量集。
3、semctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semun, int cmd, …);
用来初始化信号集,或者删除信号集。
semid:信号量集I P C 标识符。
semun:操作信号在信号集中的编号,第一个信号的号是0.
cmd:在semid指定的信号量集合上执行此命令。
第四个参数是可选的,如果使用该参数,则其类型是semun,它是多个特定命令参数的联合(union):
sem_union:可选参数,结构如下:
union semun{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
一般用到的是val,表示要传给信号量的初始值。
第三个参数cmd常用命令:
IPC_SEAT:对此集合取semid_ds 结构,并存放在由arg.buf指向的结构中。
IPC_RMID:从系统中删除该信号量集合。
SETVAL:设置信号量集中的一个单独的信号量的值,此时需要传入第四个参数。
返回值:成功返回一个正数,失败返回-1。
4、 semop函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf * sops, unsigned nsops);
功能:操作一个或一组信号。也可以叫PV操作
semid:信号集的识别码,可以通过semget获取。
sops:是一个指针,指向一个信号量操作数组。信号量操作由结构体sembuf 结构表示如下:
struct sembuf{
unsigned short sem_num; //信号在信号集中的索引,0代表第一个信号,1代表第二个信号
short sem_op; //操作类型,信号量在一次操作中需要改变的数据,通常是两个数,是-1和1,即P(V)操作
short sem_flg; //操作标志,//通常为SEM_UNDO,使操作系统跟踪信号, //并在进程没有释放该信号量而终止时,操作系统释放信号量
};
sembuf结构体参数说明:
sem_num:操作信号在信号集中的编号,第一个信号的编号是0,最后一个信号的编号是nsems-1。
sem_op:操作信号量
若sem_op 为负(P操作), 其绝对值又大于信号的现有值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权。
若sem_op 为正(V操作), 该值会加到现有的信号内值上。通常用于释放所控制资源的使用权。
sem_op的值为0:如果没有设置IPC_NOWAIT,则调用该操作的进程或线程将暂时睡眠,直到信号量的值为0;否则进程或线程会返回错误EAGAIN。
sem_flg: 信号操作标识,有如下两种选择:
IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
SEM_UNDO:程序结束时(正常退出或异常终止),保证信号值会被重设为semop()调用前的值。避免程序在异常情况下结束时未解锁锁定的资源,早成资源被永远锁定。造成死锁。
nsops:信号操作结构的数量,恒大于或等于1.
返回值:成功执行时,都会回0,失败返回-1,并设置errno错误信息。
sem_init
thread_function1
thread_function1--------------sem_wait
sleep
thread_function2--------------sem_post
sem_wait
sem_post
//代码如下
#include<iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
using namespace std;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
static int sem_id = 0;
static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();
int main(int argc, char *argv[])
{
char message = 'S';
int i = 0;
//创建信号量
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if(argc > 1)
{
//程序第一次被调用,初始化信号量
if(!set_semvalue())
{
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
//设置要输出到屏幕中的信息,即其参数的第一个字符
message = argv[1][0];
sleep(2);
}
cout<<argc<<message<<endl;
for(i = 0; i < 10; ++i)
{
//进入临界区
if(!semaphore_p())
exit(EXIT_FAILURE);
//向屏幕中输出数据
printf("进入%c", message);
//清理缓冲区,然后休眠随机时间
fflush(stdout);
sleep(rand() % 3);
//离开临界区前再一次向屏幕输出数据
printf("离开%c\n", message);
fflush(stdout);
//离开临界区,休眠随机时间后继续循环
if(!semaphore_v())
exit(EXIT_FAILURE);
sleep(rand() % 2);
}
sleep(10);
printf("\n%d - finished\n", getpid());
if(argc > 1)
{
//如果程序是第一次被调用,则在退出前删除信号量
sleep(3);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
static int set_semvalue()
{
//用于初始化信号量,在使用信号量前必须这样做
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
return 0;
return 1;
}
static void del_semvalue()
{
//删除信号量
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore\n");
else
fprintf(stdout, "已经删除信号量\n");
}
static int semaphore_p()
{
//对信号量做减1操作,即等待P(sv)
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;//P()
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
return 0;
}
return 1;
}
static int semaphore_v()
{
//这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;//V()
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_v failed\n");
return 0;
}
return 1;
}