一、有名信号量
在文件系统中有一个名字 即有一个对应的inode节点 ,但是信号量的内容(值)确实存在于内核中 ,一般可以用于任意线程或进程间。
有名信号量的操作一般有:
sem_open
sem_wait/sem_post
sem_close
sem_unlink
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
name : 要创建或打开的POSIX有名信号量在文件系统中的路径名
要求是以'/'开头的路径名(路径名中只能有1个/)
如: "/test.sem"
也就是说你的有名信号量必须在根目录下面
oflag: 打开标志
(1) 打开: 0
(2) 创建: O_CREAT
如果你是打开则只需要两个参数 如果是创建则带四个参数
mode : 创建权限位 有两种方式指定
(1)S_IRUSR S_IWUSR S_IXUSR
(2)0664
value: 制定创建的有名信号量的初始值
返回值:
成功返回一个sem_t的一个指针 指向POSIX有名信号量
失败返回SEM_FAILED 并且errno被设置
int sem_wait(sem_t *sem);
如果P操作获取不了资源
此函数会阻塞死等
返回值:
返回0 表示成功获取了该信号量
返回-1 表示出错 并且errno被设置
int sem_post(sem_t *sem);
函数功能: V操作 用来释放sem指定的POSIX信号量
int sem_close(sem_t *sem);
sem_close 用来关闭一个POSIX有名信号量
int sem_unlink(const char *name);
sem_unlink用来删除一个POSIX有名信号量
参数: 要删除的POSIX的有名信号量的路径名
实例1:父子进程间将共享内存中的值各加10万次
/**************************************************************
> File Name: sendShmget.c
> Author: LuoLiang
> Mail: 1204553475@qq.com
> Created Time: 2022年02月23日 星期三 03时16分19秒
**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <sys/wait.h>
#define PATHNAME "./test.ftok"
#define SEMPATH "/test1.sem"
int main(int argc, char **argv)
{
key_t key;
sem_t *semp = NULL;
int shmid;
int proc_id = 1;
size_t size = 4096;
int *shmp = NULL;
pid_t pid;
int n = 0;
key = ftok(PATHNAME, proc_id);
if (key < 0)
{
perror("ftok()");
exit(1);
}
shmid = shmget(key, size, IPC_CREAT | 0666);
if (shmid < 0)
{
perror("shmget()");
exit(1);
}
shmp = shmat(shmid, NULL, 0);
if (shmp == NULL)
{
perror("shmat()");
exit(1);
}
*shmp = 0;
semp = sem_open(SEMPATH, O_CREAT, 0664, 1);
if (semp == SEM_FAILED)
{
perror("sem_open()");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork()");
exit(1);
}
if (pid == 0)
{
while (n < 100000)
{
sem_wait(semp);
(*shmp)++;
sem_post(semp);
n++;
}
exit(0);
}
while (n < 100000)
{
sem_wait(semp);
(*shmp)++;
sem_post(semp);
n++;
}
wait(NULL);
printf("%d\n",*shmp);
sem_close(semp);
sem_unlink(SEMPATH);
shmdt(shmp);
exit(0);
}
二、 无名信号量
没有名字无名信号量存在于内存中,一般用于线程间 和 有亲缘关系的进程间 。
无名信号量的操作一般有:
sem_init
sem_wait/sem_post
sem_destory
函数原型:
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem: 指向要初始化的无名信号量
一般我们会先定义或者分配一个无名信号量 sem_t
pshared: 该无名信号量的共享方式
0:进程内部的线程共享
1:不同进程间的共享
value : 指定该无名信号量的初始值
int sem_wait(sem_t *sem);
如果P操作获取不了资源
此函数会阻塞死等
返回值:
返回0 表示成功获取了该信号量
返回-1 表示出错 并且errno被设置
int sem_post(sem_t *sem);
函数功能: V操作 用来释放sem指定的POSIX信号量
int sem_destroy(sem_t *sem);
sem_destory用来销毁一个POSIX的无名信号量
实例1:求共享内存中的值两个线程各加10万次
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/wait.h>
#define PATHNAME "log"
#define TIDSIZE 3
sem_t *semp;
int *shmp = NULL;
void *pthread_handler(void *args)
{
int n = 100000LL;
sem_wait(semp);
while (n--)
{
(*shmp)++;
}
sem_post(semp);
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
key_t key;
int shmid;
int proc_id = 1;
size_t size = 4096;
pthread_t tid[TIDSIZE];
key = ftok(PATHNAME, proc_id);
if (key < 0)
{
perror("ftok()");
exit(1);
}
shmid = shmget(key, size, IPC_CREAT | 0666);
if (shmid < 0)
{
perror("shmget()");
exit(1);
}
shmp = shmat(shmid, NULL, 0);
if (shmp == NULL)
{
perror("shmat()");
exit(1);
}
*shmp = 0;
semp = (sem_t *)malloc(sizeof(sem_t));
if (sem_init(semp, 0, 1) < 0)
{
perror("sem_init()");
exit(1);
}
for (int i = 0; i < 3; i++)
{
pthread_create(tid+i, NULL, pthread_handler, NULL);
}
for (int i = 0; i < 3; i++)
{
pthread_join(tid[i], NULL);
}
printf("%d\n",*shmp);
sem_destroy(semp);
shmdt(shmp);
exit(0);
}
实例二:父子进程间将共享内存中的值各加10万次
注意:在具有亲缘关系的进程中信号量也必须放在共享内存你,在fork前分配的空间也不能使用,所以要借用mmap函数来映射一块共享空间,不是很方便,尽量只在线程中使用无名信号量。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/wait.h>
#define PATHNAME "/tmp/out"
int main(int argc, char **argv)
{
sem_t *semp = NULL;
key_t key;
int shmid;
int proc_id = 1;
size_t size = 4096;
int *shmp = NULL;
pid_t pid;
int n = 100000LL;
key = ftok(PATHNAME, proc_id);
if (key < 0)
{
perror("ftok()");
exit(1);
}
shmid = shmget(key, size, IPC_CREAT | 0666);
if (shmid < 0)
{
perror("shmget()");
exit(1);
}
shmp = shmat(shmid, NULL, 0);
if (shmp == NULL)
{
perror("shmat()");
exit(1);
}
*shmp = 0;
semp = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (semp == NULL)
{
perror("mmap()");
exit(1);
}
if (sem_init(semp, 1, 1) < 0)
{
perror("sem_init()");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork()");
exit(1);
}
if (pid == 0)
{
while (n--)
{
sem_wait(semp);
(*shmp)++;
sem_post(semp);
}
exit(0);
}
while (n--)
{
sem_wait(semp);
(*shmp)++;
sem_post(semp);
}
wait(NULL);
printf("%d\n",*shmp);
sem_destroy(semp);
shmdt(shmp);
munmap(semp, sizeof(sem_t));
exit(0);
}