文章目录
多个进程同步访问同一个资源和多个线程同步访问同一个资源相同,也会存在资源抢占的情况。所以需要进行资源加锁。
1. sem_open和sem_init的区别
适用范围:sem_open
用于获取一个命名信号量,而sem_init
用于获取一个未命名的信号量
使用场景:sem_open
用于跨进程共享信号量的场景,sem_init
用于在一个进程中初始化信号量,例如在一个线程中同步访问共享资源
参数:sem_open
需要执行信号量的名称和权限,而sem_init
只需要指定信号量的地址初始参数
返回值:sem_open
返回指向信号量的指针,sem_init
没有返回值
总结下来主要是在多个进程中要使用sem_open
来创建信号量
1.1 sys/sem和semaphore
<sys/sem.h>
为 XSI(最初是 Unix System V)信号量提供接口。 这些不是基本 POSIX 标准的一部分(它们在 XSI 选项中,主要是为了传统的 Unix 兼容性),虽然它们还没有被认为是过时的/弃用的,但许多程序员认为它们已弃用。
<semaphore.h>
定义了 POSIX 信号量,它们的设计方式使得它们可以完全在用户空间中实现,除非在有争议的情况下,进程将调用内核进入睡眠状态。 它们的性能应该接近最佳(即几乎不可能击败您自己的),但它们的功能不如 XSI 信号量
它们彼此不同/替代,而是两种不同的实现并提供不同的功能集。semaphore.h是posix实现和sys/sem.h是 sysV 的实现。 POSIX 被 认为 更轻量级并被广泛使用。
2.使用方法
使用起,从创建到销毁来大概分为四步
2.1 创建命名信号量sem_open
sem_t *sem_open(const char *name, int oflag, ...);
参数:
name:信号量的名字;
oflag:打开标志,常用的有 O_CREAT 和 O_EXCL。O_CREAT 表示如果名字的信号量不存在则创建它,而 O_EXCL 则在创建新信号量时使用,如果名字的信号量已经存在则返回错误
...:代表当使用 O_CREAT 标志时,用于指定信号量初始值的参数。如果没有使用 O_CREAT,则此参数被忽略。
2.2等待信号量sem_wait
sem_wait() 用于等待信号量,它会使调用进程等待,直到信号量的值大于零,其定义如下:
int sem_wait(sem_t *sem);
@参数
sem;指向信号量的指针
@返回值
若函数执行成功,返回 0,失败返回-1。
2.3 释放信号量sem_post
sem_post()用于当一个进程完成对共享资源的访问后释放信号量,这样其他等待该信号量的进程就可以继续执行。其定义如下:
int sem_post(sem_t *sem);
@参数
sem;指向信号量的指针
@返回值
若函数执行成功,返回 0,失败返回-1。
2.4关闭信号量sem_close
sem_close()
用于关闭已打开的信号量,其定义如下:
int sem_close(sem_t *sem);
其中,sem为指向已打开的信号量的指针。
若函数执行成功,返回 0,失败返回-1。
2.5删除命名信号量sem_unlink
sem_unlink() 用于当一个命名信号量不再需要时,将其从系统中删除。其定义如下:
int sem_unlink(const char *name);
name为信号量的名字。
若函数执行成功,返回 0,失败返回-1。
一个进程使用sem_unlink
删除信号量后,另一个进程仍然可以尝试使用sem_unlink
。这是因为当一个进程调用sem_unlink
时,它会尝试删除指定的信号量名。但是,这个删除操作并不会立即生效,而是要等到所有其他进程都关闭了该信号量之后,信号量才会被真正删除。
但是当进程异常退出而没有调用sem_close
时,信号量是无法被系统回收的
2.6 sem_close
和sem_unlink
的差别
-
sem_close
函数用于关闭一个已经打开的有名信号量。当一个进程终止时,内核会自动对其上仍然打开着的所有有名信号量执行sem_close
操作。关闭一个信号量并不会将它从系统中删除,而是结束对该信号量的引用。因此,sem_close
操作只是结束了当前进程对信号量的使用,但信号量本身并未被删除12。 -
sem_unlink
函数则用于从系统中删除一个有名信号量。每个信号量有一个引用计数器记录打开的次数,当引用计数大于0时,信号量的名字可以从文件系统中删除,然而其析构却要等到最后一个sem_close
发生时为止。因此,sem_unlink
操作实际上是从文件系统中移除信号量的记录,而真正的析构发生在最后一个对信号量的sem_close
操作之后1。
简而言之,sem_close
主要是结束进程对信号量的使用,而sem_unlink
则是从系统中移除信号量的记录,两者共同作用,确保了信号量的正确管理和资源的有效利用。
3.代码实例
read.exe
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define KEY_NUM 126
#define FILE_PATH "./share.txt"
//使用文件路径作为共享内存
int shm_create_with_key(const char *filepath, int len, int key_num)
{
key_t key = ftok(filepath, key_num);
if(key < 0)
{
printf("create key error!\n");
return key;
}
int shmid = shmget(key, len, IPC_CREAT|0666);//666代表可读可写
if(shmid < 0)
{
return shmid;
printf("create share mem error!\n");
}
return shmid;
}
int shm_create(int len)
{
return shm_create_with_key(FILE_PATH, 100, KEY_NUM);
}
char* shm_connect(int shm_id)
{
char *mem = (char*)shmat(shm_id, NULL, 0);
if(mem < 0)
{
printf("shmat error!\n");
return NULL;
}
return mem;
}
int main()
{
//创建打开一个信号量
sem_t *sem = sem_open("m_sem2", O_CREAT, 0666, 1);
if(sem == NULL)
{
printf("sem_open error!\n");
return -1;
}
int shm_id = shm_create(10);
if(shm_id < 0)
{
printf("shm_create error!\n");
return -1;
}
char *mem = shm_connect(shm_id);
if(!mem)
{
printf("shm_connect error!\n");
return -1;
}
int i = 5;
while (--i)
{
int num=0;
if(0 == sem_getvalue(sem, &num) && 0 == num)
{
printf("read.exe waiting resource\n");
}
sem_wait(sem);
printf("%s\n", mem);
sem_post(sem);
sleep(1);
}
shmdt(mem);//释放共享内存
shmctl(shm_id, IPC_RMID, NULL);//删除共享内存
sem_close(sem);//关闭信号量
sem_unlink("m_sem2");//删除信号量
return 0;
}
write.exe
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "string.h"
#define KEY_NUM 126
#define FILE_PATH "./share.txt"
int shm_create_with_key(const char *filepath, int len, int key_num)
{
key_t key = ftok(filepath, key_num);
if(key < 0)
{
printf("create key error!\n");
return key;
}
int shmid = shmget(key, len, IPC_CREAT|0666);//666代表可读可写
if(shmid < 0)
{
return shmid;
printf("create share mem error!\n");
}
return shmid;
}
int shm_create(int len)
{
return shm_create_with_key(FILE_PATH, 100, KEY_NUM);
}
char* shm_connect(int shm_id)
{
char *mem = (char*)shmat(shm_id, NULL, 0);
if(mem < 0)
{
printf("shmat error!\n");
return NULL;
}
return mem;
}
int main()
{
sem_t *sem = sem_open("m_sem2", O_CREAT, 0666, 1);
if(NULL == sem)
{
printf("sem_open error\n");
return -1;
}
int shm_id = shm_create(10);
if(shm_id < 0)
{
printf("shm_create error!\n");
return 0;
}
char *mem = shm_connect(shm_id);
if(!mem)
{
printf("shm_connect error!\n");
return 0;
}
//初始化
memcpy(mem, "hello", 6);
int i = 5;
while (--i)
{
sleep(1);
int num=0;
if(0 == sem_getvalue(sem, &num) && 0 == num)
{
printf("write.exe waiting resource\n");
}
sem_wait(sem);
mem[0] = '1'+i;
printf("write succ\n");
sleep(2);
sem_post(sem);
}
shmdt(mem);
sem_close(sem);
sem_unlink("m_sem2");
return 0;
}
s