目录
一 信号量的定义
二 信号量的 *** 作
2.1 信号量的创建
2.2 信号量的PV *** 作
2.3 信号量的设置
三.测试案例
一 信号量的定义
信号量的出现,主要是为了解决进程之间对于临界资源的独占式访问的问题。信号量可以满足对于临界资源的访问只能有一个进程。
二 信号量的 *** 作LINUX的信号量API都定义在sys/sem.h头文件中:
2.1 信号量的创建int semget(key_t key, int num_sems, int sem_flags);
函数功能:创建或者获取由键值key标识的信号量集合。
函数参数:
key:主要是用于在全局唯一的表示一个信号量集合,相当于全局的文件名一样。
num_sems:主要是用于设置所创建信号量集合中信号量的个数;用于获取的时候可以不用设置。
sem_flags:主要讲解几个参数:
IPC_CREAT:如果该key值表示的信号量集不存在,则创建;如果存在,则获取;IPC_EXCL:只能和其他参数联合使用,如果和IPC_CREAT联合使用,则当key值表示的信号量集合存在,则返回错误:EEXIST。当创建成功时,会在内核中创建一个变量和该信号量关联:
struct semid_ds{
struct ipc_perm sem_perm;
unsigned long int sem_nsems;
time_t sem_otime;
time_t sem_ctime;
};
其中,ipclass="superseo">c_perm是LINUX内核用于表示所有IPC变量(信号量,共享内存以及消息队列)以及其相关权限的结构体:
struct ipc_perm{
key_t key;//用于全局标识唯一的IPC变量
uid_t uid;//用户id
gid_t gid;//用户组id
uid_t cuid;//创建者id
gid_t cgid;//创建者所在组id
mode_t mode;//访问权限
};
2.2 信号量的PV *** 作
首先来讲一下和每个信号量相关的内核变量:
unsigned short semval;//该信号量的值
unsigned short semncnt;//等待该信号量不为0的进程数
unsigned short semzcnt;//等待该信号量为0的进程数
pid_t sempid;//上一次修改该信号量值的进程PID
PV *** 作的函数如下所示:
int semop(int sem_id, struct sembuf* sem_ops, size_t num_sem_ops);
函数功能:主要是对该信号量进行PV *** 作。
函数参数:
sem_id:semget()函数返回的信号量集标识符。sem_ops:指向一个sembuf类型的结构体数组:struct sembuf{
unsigned short int sem_num;//该信号量在信号量集中的位置
short int sem_op;//相应的对于该信号量的加减 *** 作的值
short int sem_flg;
};
3.num_sem_ops:要执行 *** 作的个数。
具体 *** 作流程:
a. 当sem_op大于0时,将会对该信号量集中对应信号量的值加sem_op;
b. 当sem_op小于0时,将会对该信号量集中对应信号量的值减去sem_op;
当信号量的当前值semval小于等于sem_op,则 *** 作成功;
当信号量的当前值semval大于sem_op,则 *** 作失败:如果sem_flg的值设为IPC_NOWAIT,则会返回错误EAGAIN,不会阻塞等待;如果sem_flg的值未设为IPC_NOWAIT,则会阻塞等待,阻塞等待结束条件是:semval值增加到大于等于sem_op;该信号量集被进程回收,会返回错误EIDRM;该semop()函数被中断,返回错误EINTR。
c. 当sem_op等于0时,则会等待该信号的semval值为0:
当信号量的当前值semval等于0,则 *** 作成功;
当信号量的当前值不为0,则 *** 作失败:如果sem_flg设为IPC_NOWAIT,则会返回错误EAGAIN,不会阻塞等待;如果sem_flg的值未设为IPC_NOWAIT,则会阻塞等待,阻塞等待结束条件是:semval值变为0;信号量集被进程回收,返回错误EIDRM;semop() *** 作被中断,返回错误EINTR。
2.3 信号量的设置int semctl(int sem_id, int sem_num, int command, union semun);
函数功能:用于给sem_id标识的信号量集进行设置。
函数参数:
a.用户自己定义的参数,一般格式为:
union semun{
int val;
struct semid_ds* buf;
unsigned int* array;
struct seminfo* _buf;
};
b.command:一个指令,常用指令如下:
IPC_STAT:用于将该sem_id标识的信号量集的所有信号量结构体semid_ds值存入buf中;
IPC_SET:用于设置sem_id标识信号量集的所有信号量结构体semid_ds值为buf;
GETVAL:用于将sem_id标识的信号量集中的第sem_num个信号量的值semval存入val中;
SETVAL:与GETVAL相反;
GETALL:用于将sem_id标识的信号量集中的所有信号量的值semval存入array中;
SETALL:与GETALL相反。
三.测试案例下面我将要测试信号量对于代码执行过程的影响,代码如下所示:
#include
#include
#include
#include
#include
union semun{
int val;
struct semid_ds* buf;
unsigned short int* array;
struct seminfo* _buf;
};
void pv(int sem_id, int sem_op)
{
struct sembuf sem_ops;
sem_ops.sem_num = 0;
sem_ops.sem_op = sem_op;
sem_ops.sem_flg = SEM_UNDO;
semop(sem_id, &sem_ops, 1);
}
int main()
{
printf("开始:\n");
int sem_id = semget(IPC_PRIVATE, 1, 0666);//创建信号量集(其中只有一个信号量)
union semun sem_un;
sem_un.val = 1;//信号量初始值设为1,这里只测试二元信号量
semctl(sem_id, 0, SETVAL, sem_un);
pid_t pid = fork();
if(pid < 0)
return -1;
else if(pid == 0)
{
printf("这是子进程.\n");
pv(sem_id, -1);//进入临界区
printf("子进程休息.\n");
sleep(5);
printf("子进程休息完毕.\n");
pv(sem_id, 1);//退出临界区
exit(0);//终止子进程
}
else
{
printf("这是父进程.\n");
pv(sem_id, -1);//进入临界区
printf("父进程休息.\n");
sleep(5);
printf("父进程休息完毕.\n");
pv(sem_id, 1);//退出临界区
}
waitpid(pid, NULL, 0);//回收子进程
semctl(sem_id, 0, IPC_RMID, sem_un);//删除信号量
return 0;
}
1.当加入pv *** 作时,代码执行结果如下所示:
2.当注释pv *** 作时,代码执行结果如下所示:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)