ftok
通过文件路径 pathname 以及 proj_id 获得键值key(键值:信号灯集的标识),只要文件路径和proj_id不变,key值就不变
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
原型:
key_t ftok(const char *pathname, int proj_id);
参数:
char *pathname:文件路径(目录文件或是普通文件);
int proj_id:非0参数,用户自定义;
返回值:
成功,返回key值;
失败,返回-1,更新errno;
例程:
key_t key = ftok("./", 'a');
if(key < 0)
{
perror("ftok");
return -1;
}
printf("key = %x\n", key);
semget
创建或打开信号灯集对象,创建成功后信号灯集的所有信号量的值默认初始化为0
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
原型:
int semget(key_t key, int nsems, int semflg);
参数:
key_t key:键值,提前约定或ftok函数创建获得;
int nsems:信号灯集对象中包含的信号量个数;
int semflg:设置信号灯集的访问权限,参数如下:
IPC_CREAT:若信号灯集对象不存在则创建,否则打开已经存在的信号灯集对象;
IPC_EXCL:若信号灯集对象不存在则创建,否则返回错误;
IPC_NOWAIT:(非阻塞)如果本操作需要等待,则直接返回错误;
IPC_CREAT|0666 如果内核中不存在与key值相同的信号灯集,则创建信号灯集,并设置8进制权限(按位或)。如果存在,则直接返回信号灯集标识符semid;
返回值:
成功,返回信号灯集标识 semid;
失败,返回-1,更新errno;
例程:
int semid = semget(key, 2, IPC_CREAT|0777);
if(semid < 0)
{
perror("semget");
return -1;
}
printf("semid = %d\n", semid);
semop
对信号灯集中指定信号量的PV操作:申请信号量P(-1)|释放信号量V(+1)
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
原型:
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数:
int semid:信号灯集对象id;
struct sembuf *sops:指向sembuf结构体的指针,操作信号灯集中的信号量
struct sembuf
{
unsigned short int sem_num; /* semaphore number */信号灯编号,从0开始编
short int sem_op; /* semaphore operation */
正数,例如+1或+n,释放信号量(V操作),增加信号量的值
负数,例如-1或-n,申请信号量(P操作),减小信号量的值
0,阻塞,直到信号量的值为0
short int sem_flg; /* operation flag */操作控制标志
0:以阻塞方式运行
IPC_NOWAIT:当指定的PV操作不能完成时,进程不会被阻塞,立即返回-1,errno设置为EAGAIN;
SEM_UNDO:以阻塞方式运行;进程异常退出时,执行信号量解除操作;例如:进程执行了P操作后异常退出,如果sem_flg设置了SEM_UNDO,则内核会对该进程执行V操作,保证安全性;
};
unsigned nsops:操作结构体struct sembuf对象的数量;
返回值:
成功,返回0;
失败,返回-1,更新errno;
例程:
struct sembuf sops;//定义结构体struct sembuf对象
sops.sem_num = 0;//操作0号灯
sops.sem_op = -1;//申请信号量
sops.sem_flg = 0;//以阻塞方式运行
if(semop(semid, &sops, 1) < 0)
{
perror("semop");
return -1;
}
printf("P操作成功 semval:1\n");
semctl
信号灯集的控制(信号量初始化、信号量值操作、信号灯集删除操作)
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
原型:
int semctl(int semid, int semnum, int cmd, ...);
参数:
int semid:信号灯集对象id;
int semnum:指定要控制的信号灯的编号;(如果控制是针对整个信号灯集,该值设为0)
int cmd:要执行的控制命令:
IPC_RMID:删除信号灯集,第四个参数不填。第二个参数,随便填int类型数据,设置为0;
IPC_STAT:获取信号灯集的属性(struct ipc_perm数据项),存放在第四个参数(struct semid_ds中的sem_perm数据项中);
IPC_SET:设置信号灯集的属性(struct ipc_perm数据项),存放在第四个参数(struct semid_ds中的sem_perm数据项中);
GETVAL:获取指定信号灯的值,第四个参数不填;
SETVAL:设置指定信号灯的值;这个值通过union semun中的val成员来指定;
GETALL:获取信号灯集中所有信号灯的值,存储在第四个参数中。第二个参数,随便填int类型数据,设置为0;
SETALL:设置信号灯集中所有信号灯的值,存储在第四个参数中(一般用于信号量初始化设置初始值)。第二个参数,随便填int类型数据,设置为0;
GETPID:获取信号灯拥有者进程的PID值;
...:不定参数:不同的cmd,该参数不同;与控制命令cmd配合的参数如下:
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
返回值:
成功,返回值大于或等于0;
失败,返回-1,更新errno;
例程:
#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//申请key值
key_t key = ftok("./", 'c');
if(key < 0)
{
perror("ftok");
return -1;
}
printf("key = %x\n", key);
//创建信号灯集
int semid = semget(key, 2, IPC_CREAT|0777);
if(semid < 0)
{
perror("semget");
return -1;
}
printf("semid = %d\n", semid);
//SETALL 设置所有信号灯的值
unsigned short setall[2] = {2, 1};
if(semctl(semid, 0, SETALL, setall) < 0)
{
perror("semctl");
return -1;
}
printf("0:%d 1:%d\n", setall[0],setall[1]);
//GETALL 获取所有信号灯的值
unsigned short getall[2];
if(semctl(semid, 0, GETALL, getall) < 0)
{
perror("semctl");
return -1;
}
printf("0:%d 1:%d\n", getall[0],getall[1]);
//SETVAL 设置单个信号灯的值
int setval = 5;
if(semctl(semid, 1, SETVAL, setval) < 0)
{
perror("semctl");
return -1;
}
printf("设置成功\n");
//GETVAL 获取单个信号灯的值
int getval = semctl(semid, 1, GETVAL);
printf("getval=%d\n", getval);
//IPC_RMID 删除信号灯
if(semctl(semid, 0, IPC_RMID) < 0)
{
perror("semctl");
return -1;
}
printf("删除信号灯成功\n");
}
结果:
key = 63190789
semid = 0
0:2 1:1
0:2 1:1
设置成功
getval=5
删除信号灯成功
应用例程
创建两个没有亲缘关系的进程,一个进程倒置字符串,一个进程打印字符串。倒置一次打印一次。提示,使用共享内存,及信号灯集, 信号(pause)。
打印进程
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int main(int argc, const char *argv[])
{
//创建有名管道
if(mkfifo("./myfifo", 0664) < 0)
{
if(errno != EEXIST)
{
perror("mkfifo");
return -1;
}
}
//只读打开有名管道
int fd = open("./myfifo", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
//读取管道中倒置进程的pid
pid_t pid;
if(read(fd, &pid, sizeof(pid)) < 0)//倒置进程若没有启动,read阻塞
{
perror("read");
return -1;
}
printf("pid = %d\n", pid);
close(fd);
/***初始化共享内存及信号灯集合*****/
//共享内存
//1.key
key_t key = ftok("./", 'e');
if(key<0)
{
perror("ftok");
return -1;
}
//2.创建共享内存
int shmid = shmget(key, 128, IPC_CREAT|0777);
if(shmid < 0)
{
perror("shmget");
return -1;
}
//3.映射
void* shmaddr = shmat(shmid, NULL, 0);
if((void*)-1 == shmaddr)
{
perror("shmat");
return -1;
}
//str即为共享内存空间的首地址
char* str = (char*)shmaddr;
//4.写入共享内存
strcpy((char*)shmaddr, "AAAAA|BBBBB");
//信号灯集合
//1.key 不同的通信机制可共用一个key
//2.创建信号灯集
int semid = semget(key, 2, IPC_CREAT|0777);
if(semid < 0)
{
perror("semget");
return -1;
}
//3.设置信号灯的值
//0号灯控制打印,1号灯控制倒置
//初始0号灯非阻塞,1号灯阻塞
unsigned short setall[2] = {1, 0};
if(semctl(semid, 0, SETALL, setall) < 0)
{
perror("semctl");
return -1;
}
//唤醒倒置进程
if(kill(pid, 10) < 0)
{
perror("kill");
return -1;
}
//0号灯控制打印,1号灯控制倒置
struct sembuf sops;
while(1)
{
//0号灯的P操作
sops.sem_num = 0; //0号灯
sops.sem_op = -1; //P操作
sops.sem_flg = 0; //阻塞方式运行
if(semop(semid, &sops, 1)<0)
{
perror("semop");
return -1;
}
printf("%s\n", str);
sleep(1);
//1号灯的V操作
sops.sem_num = 1; //1号灯
sops.sem_op = +1; //V操作
sops.sem_flg = 0;
if(semop(semid, &sops, 1)<0)
{
perror("semop");
return -1;
}
}
return 0;
}
倒置进程
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
void handler(int sig){}
int main(int argc, const char *argv[])
{
//注册信号处理函数
sighandler_t s = signal(10, handler);
if(SIG_ERR == s)
{
perror("signal");
return -1;
}
//创建有名管道
if(mkfifo("./myfifo", 0664) < 0)
{
if(errno != EEXIST)
{
perror("mkfifo");
return -1;
}
}
//只写打开有名管道
int fd = open("./myfifo", O_WRONLY);
if(fd < 0)
{
perror("open");
return -1;
}
//向管道中写入倒置进程的pid
pid_t pid = getpid();
printf("pid=%d\n", pid);
if(write(fd, &pid, sizeof(pid)) < 0)
{
perror("write");
return -1;
}
close(fd);
//阻塞,等待打印进程唤醒信号(信号10)
pause();
//共享内存
//1.key
key_t key = ftok("./", 'e');
if(key<0)
{
perror("ftok");
return -1;
}
//2.打开共享内存
int shmid = shmget(key, 128, IPC_CREAT|0777);
if(shmid < 0)
{
perror("shmget");
return -1;
}
//3.映射
void* shmaddr = shmat(shmid, NULL, 0);
if((void*)-1 == shmaddr)
{
perror("shmat");
return -1;
}
//读取共享内存数据 str即为共享内存空间的首地址
char* str = (char*)shmaddr;
//信号灯集合
//1.key 不同的通信机制可共用一个key
//2.打开信号灯集
int semid = semget(key, 2, IPC_CREAT|0777);
if(semid < 0)
{
perror("semget");
return -1;
}
//0号灯控制打印,1号灯控制倒置
struct sembuf sops;
int size = strlen(str);
while(1)
{
//1号灯的P操作
sops.sem_num = 1; //1号灯
sops.sem_op = -1; //P操作
sops.sem_flg = 0; //阻塞方式运行
if(semop(semid, &sops, 1)<0)
{
perror("semop");
return -1;
}
int i = 0;
for(i=0; i<size/2; i++)
{
char temp = str[i];
str[i] = str[size-1-i];
str[size-1-i] = temp;
}
//0号灯的V操作
sops.sem_num = 0; //0号灯
sops.sem_op = +1; //V操作
sops.sem_flg = 0;
if(semop(semid, &sops, 1)<0)
{
perror("semop");
return -1;
}
}
return 0;
}
结果
本文链接:https://shengto.top/c/sem_routine.html
转载时须注明出处及本声明