信号量
1.创建或获取一个信号量集标识符
int semget(key_t key, int nsems, int semflg)
key:同上
nsems:信号量集中信号量个数
semflg:0664|IPC_CREAT
返回值:成功:返回信号量集的标识符;失败返回-1,错误原因存于error中
2.初始化信号量集
int semctl(int semid, int semnum, int cmd, union semun arg)
semid:信号量集标识符
semnum:信号量集数组上的下标,表示某一个信号量
cmd:控制选项,IPC_RMID和SETVAL最常用
IPC_STAT、IPC_SET、IPC_RMID 都与消息队列和共享内存类似。
SETVAL:通过第四个参数,给集合中semnum编号的信号量设置一个int初值。
需要自己定义;
union semun {
short val; /*SETVAL用的值*/
struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构**/
unsigned short* array; /*SETALL、GETALL用的数组值*/
struct seminfo *buf; /*为控制IPC_INFO提供的缓存*/
} arg;
联合体使用实例:
1.当需要指定struct semid_ds缓存时:
union semun sem_un;
struct semid_ds buff;
sem_un.buf = &buff;
semctl(semid, 0, IPC_STAT, sem_un);
2.当需要指定信号量的int初始值时:
union semun sem_un;
sem_un.val = 1;
semctl(semid, 0, IPC_STAT, sem_un);
设置初始值:
semctl(semid, semnum, SETVAL, val);
删除信号量:
for(int i = 0; i < nsems; i++)
semctl(semid, i, IPC_RMID);
3.完成对信号量的P(-1)操作或V(+1)操作
int semop(int semid, struct sembuf *sops, unsigned nsops)
semid:信号量集标识符
sops:等价于struct sembuf sops[]
nsops:指定数组元素个数
//不需要自己定义,包含在semop头文件中
struct sembuf {
unsigned short sem_num;//信号量编号
short sem_op;//为-1,p操作;+1,V操作
short sem_flg;//IPC_NOWAIT不阻塞,SEM_UNDO:防止死锁
}
利用信号量实现同步:
#include <stdio.h>
#include "securec.h"
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<signal.h>
#include<strings.h>
#include<string.h>
#define NSEMS 3
#define SEM_FILE "./semfile"
int semid;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
void print_err(char *str){
perror(str);
exit(-1);
}
void signal_fun(int signo) {
del_sem(semid, NSEMS);
exit(-1);
}
int create_or_get_sem(int nsems) {
int semid = -1;
int fd = -1;
key_t key = 0;
fd = open(SEM_FILE, O_RDWR|O_CREAT, 0664);
if (fd == -1) print_err("open fail");
key = ftok(SEM_FILE, 'a');
if (key == -1) print_err("ftok fail");
semid = semget(key, nsems, 0664|IPC_CREAT);
if (semid == -1) print_err("semget fail");
return semid;
}
void init_sem(int semid, int semnum, int val) {
int ret = -1;
union semun sem_un;
/* semnum:信号量编号
SETVAL:设置信号量初始值cmd
sem_un:初始值
*/
sem_un.val = val;
ret = semctl(semid, semnum, SETVAL, sem_un.val);
if (ret == -1) print_err("semctl fail");
}
void del_sem(int semid, int nsems) {
int ret = -1;
ret = semctl(semid, i, IPC_RMID); //调用一次就会把信号量集合删除
if (ret == -1) print_err("semctl fail");
}
void p_sem (int semid, int semnum_buf[], int nsops) {
/*int semop(int semid, struct sembuf *sops, unsigned nsops)
semid:信号量集标识符
sops:等价于struct sembuf sops[]
nsops:指定数组元素个数*/
/*//不需要自己定义,包含在semop头文件中
struct sembuf {
unsigned short sem_num;//信号量编号
short sem_op;//为-1,p操作;+1,V操作
short sem_flg;//IPC_NOWAIT不阻塞,SEM_UNDO:防止死锁
}*/
int i =0;
int ret = -1;
struct sembuf sops[nsops];
for (int i = 0; i < nsops; i++) {
sops[i].sem_num = semnum_buf[i];//信号量编号
sops[i].sem_op = -1;//p操作
sops[i].sem_flg = SEM_UNDO;//SEM_UNDO:防止死锁
}
ret = semop(semid, sops, nsops);
if (ret == -1) print_err("semop fail");
}
void v_sem (int semid, int semnum_buf[], int nsops) {
/*int semop(int semid, struct sembuf *sops, unsigned nsops)
semid:信号量集标识符
sops:等价于struct sembuf sops[]
nsops:指定数组元素个数*/
/*//不需要自己定义,包含在semop头文件中
struct sembuf {
unsigned short sem_num;//信号量编号
short sem_op;//为-1,p操作;+1,V操作
short sem_flg;//IPC_NOWAIT不阻塞,SEM_UNDO:防止死锁
}*/
int i =0;
int ret = -1;
struct sembuf sops[nsops];
for (int i = 0; i < nsops; i++) {
sops[i].sem_num = semnum_buf[i];//信号量编号
sops[i].sem_op = 1;//V操作
sops[i].sem_flg = SEM_UNDO;//SEM_UNDO:防止死锁
}
ret = semop(semid, sops, nsops);
if (ret == -1) print_err("semop fail");
}
int main()
{
int i = 0;
int ret = -1;
int semnum_buf[1] = {0}; //每次只操作一个信号,所以元素个数为1
// 创建信号量集合
semid = create_or_get_sem(NSEMS);
// 初始化信号量集合中的每个信号量
// 只需要一个进程来初始化,此处由父进程初始化
for (i = 0; i < NSEMS; i++) {
if (i == 0) {
init_sem(semid, i, 1);
} else {
init_sem(semid, i, 0);
}
}
ret = fork();
if (ret > 0) {
ret = fork();
if (ret > 0) {
// 父进程
signal(SIGINT, signal_fun);
while(1) {
semnum_buf[0] = 2;
p_sem(semid, semnum_buf, 1);
printf("333333\n");
sleep(1);
semnum_buf[0] = 1;
v_sem(semid, semnum_buf, 1);
}
} else if (ret == 0) {
// 子进程2
while(1) {
semnum_buf[0] = 1;
p_sem(semid, semnum_buf, 1);
printf("222222\n");
sleep(1);
semnum_buf[0] = 2;
v_sem(semid, semnum_buf, 1);
}
}
} else if (ret == 0) {
// 子进程1
while(1) {
semnum_buf[0] = 0;
p_sem(semid, semnum_buf, 1);
printf("111111\n");
sleep(1);
semnum_buf[0] = 1
v_sem(semid, semnum_buf, 1);
}
}
return 0;
}
sem1.c:
#include <stdio.h>
#include "securec.h"
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<signal.h>
#include<strings.h>
#include<string.h>
#define SHM_FILE "./file.txt"
#define SHM_SIZE 4096//一页为4k,即4096
#define NSEMS 2
int shmid = -1;
void *shmaddr = NULL;
int semid;
void print_err(char *str) {
perror(str);
exit(-1);
}
void signal_fun(int signo) {
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
remove("./fifo.txt");
remove(SHM_FILE);
exit(-1);
}
int get_peer_PID() {
int ret = -1;
int fifofd = -1;
/* 创建有名管道文件 */
ret = mkfifo("./fifo.txt", 0664);
if(ret == -1 && errno == EEXIST) print_err("mkfifo fail");
/* 以只读方式打开管道 */
fifofd = open("./fifo.txt", O_RDONLY);
if(fifofd == -1) print_err("open fail");
/* 读管道,获取读共享内存进程的PID */
int peer_pid;
ret = read(fifofd, &peer_pid, sizeof(peer_pid));
if (ret == -1) print_err("read fail");
return peer_pid;
}
void create_or_open_shm(void) {
key_t key = -1;
int fd = 0;
fd = open(SHM_FILE, O_RDWR|O_CREAT, 0664);
if (fd == -1) print_err("open fail");
/*利用存在的文件路径名和八位整形数,计算出key*/
key = ftok(SHM_FILE, 'b');
if (key == -1) print_err("ftok fail");
/*利用key创建、或者获取共享内存*/
shmid = shmget(key, SHM_SIZE, 0664|IPC_CREAT);
if (shmid == -1) print_err("shmget fail");
}
char buf[300] = {"aaaaaaaaaaaaaaasdddddddddddddddfffffffffffffggggggggggggg"};
int main(int argc, char **argv)
{
int peer_id = -1;
int semnum_buf[1] = {0}; //P和V操作时只需要操作一个信号量。
/* 给SIGINT信号注册捕获函数,用于删除共享内存、管道、文件等 */
signal(SIGINT, signal_fun);
semid = create_or_get_sem(NSEMS);
/* 创建或者获取共享内存 */
create_or_open_shm();
/* 建立映射 */
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) print_err("shmat error");
//写内存
while(1) {
/*写内容到共享内存*/
// p sem 0
semnum_buf[0] = 0;
p_sem(semid, semnum_buf, 1);
memcpy(shmaddr, buf, sizeof(buf));
sleep(1);
// v sem 1
semnum_buf[0] = 1;
v_sem(semid, semnum_buf, 1);
}
return 0;
}
sem2.c
#include <stdio.h>
#include "securec.h"
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<signal.h>
#include<strings.h>
#include<string.h>
#define SHM_FILE "./file.txt"
#define SHM_SIZE 4096//一页为4k,即4096
#define NSEMS 2
int shmid = -1;
void *shmaddr = NULL;
int semid;
void print_err(char *str) {
perror(str);
exit(-1);
}
void signal_fun(int signo) {
if (SIGINT == signo) {
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
remove("./fifo.txt");
remove(SHM_FILE);
exit(-1);
} else if (SIGUSR1 == signo) {
}
}
void snd_self_PID() {
int ret = -1;
int fifofd = -1;
ret = mkfifo("./fifo.txt", 0664);
if (ret == -1 && errno == EEXIST) print_err("mkfifo fail");
/* 以只写方式打开文件 */
fifofd = open("./fifo.txt", O_WRONLY);
if (fifofd == -1) print_err("open fail");
int pid = getpid();
ret = write(fifofd, &pid, sizeof(pid));//发送PID
if (ret == -1) print_err("write fail");
}
void create_or_open_shm(void) {
key_t key = -1;
int fd = 0;
fd = open(SHM_FILE, O_RDWR|O_CREAT, 0664);
if (fd == -1) print_err("open fail");
/*利用存在的文件路径名和八位整形数,计算出key*/
key = ftok(SHM_FILE, 'b');
if (key == -1) print_err("ftok fail");
/*利用key创建、或者获取共享内存*/
shmid = shmget(key, SHM_SIZE, 0664|IPC_CREAT);
if (shmid == -1) print_err("shmget fail");
}
int main(int argc, char **argv)
{
int semnum_buf[1] = {0};
/* 给SIGUSR1注册一个空捕获函数,用于唤醒pause函数 */
signal(SIGUSR1, signal_fun);
signal(SIGINT, signal_fun);
semid = create_or_get_sem(NSEMS);
/* 创建或者获取共享内存 */
create_or_open_shm();
/* 建立映射 */
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) print_err("shmat error");
//读内存
while(1) {
// p sem 1
semnum_buf[0] = 1;
p_sem(semid, semnum_buf, 1);
printf("%s\n", (char*)shmaddr);
bzero(shmaddr, SHM_SIZE);
// v sem 0
semnum_buf[0] = 0;
v_sem(semid, semnum_buf, 1);
//这里写内存有缺陷:如果没有数据会一直判断
//改进:同步!!!当共享内存没有数据时,读进程休眠,当写进程把数据写完后,将读进程唤醒。
//实现同步的方法:(1)信号;(2)信号量
/*
if (strlen((char*)shmaddr) != 0) {
printf("%s\n", (char*)shmaddr);
bzero(shmaddr, sizeof(shmaddr));
}*/
}
return 0;
}