承接上文:程序设计:信号量 写优先的读写互斥对象(完整源码 代码详解)-CSDN博客
完整源码在上文已经给出。本文解释其中的主要代码。以下代码删除了一些辅助功能。
目录
一、核心数据
二、创建信号量
三、销毁信号量
四、连接到信号量
五、获取信号量创建时间
六、读锁定
七、写锁定
八、释放读锁定
九、释放写锁定
int sem_id{ -1 };//信号量ID
一切都围绕这个进行。sem_id是系统的信号量的标识,所有信号量操作都针对这个。
这个值通过创建(semget)获得,然后把获得的ID存储在合适的位置,其它程序可以通过ID操作这个信号量。
//创建新信号量
bool Create()
{
#ifdef _LINUXOS
sem_id = semget(IPC_PRIVATE, 4, IPC_CREAT | IPC_EXCL | 0666);
#else
sem_id = semget(IPC_PRIVATE, 4, IPC_CREAT | IPC_EXCL | SEM_R | (SEM_R >> 3) | (SEM_R >> 6) | SEM_A | (SEM_A >> 3) | (SEM_A >> 6));
#endif
if (-1 == sem_id)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
return false;
}
union semun sem;
sem.val = 1;
if (semctl(sem_id, 0, SETVAL, sem) < 0)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
return false;
}
sem.val = 0;
if (semctl(sem_id, 1, SETVAL, sem) < 0)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
return false;
}
sem.val = 0;
if (semctl(sem_id, 2, SETVAL, sem) < 0)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
return false;
}
sem.val = 0;
if (semctl(sem_id, 3, SETVAL, sem) < 0)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
return false;
}
return true;
}
这个代码首先创建一个信号量,包含四个值。第一个参数key为IPC_PRIVATE,表示由系统分配。由于信号量是全系统范围的,因此必须保证在全系统范围内没有冲突。对于socket端口,由国际组织分配服务端口号,而对于信号量,并没有什么国际组织管理这个,所以我总是使用系统分配。还有些老代码喜欢用磁盘路径创建一个唯一值作为key,但是如果路径被删除重建,key就会改变,从而导致互斥失败。所以归根结底,没有靠谱的方法。
创建信号量之后就要初始化每个值,然后才能使用。
一般情况下创建了就不用删除了,除非发生了BUG。
//删除信号量
bool Destory()
{
union semun sem;
sem.val = 0;
if (semctl(sem_id, 0, IPC_RMID, sem) < 0)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
return false;
}
else
{
sem_id = -1;
return true;
}
}
这个功能只是设置这个类要操作的sem_id而已。作为一个实用代码,做了一些检查,以确认这个信号量是自己系统的信号量,而不是别的系统的。
bool Attach(int id, time_t const& ctime)
{
sem_id = id;
//检查
time_t tmp_ctime;
if (!_GetCTime(tmp_ctime))
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << errno2str();
sem_id = -1;
return false;
}
else
{
if (tmp_ctime != ctime)
{
m_errid = __LINE__;
m_errmsg.str("");
m_errmsg << "ctime不匹配";
sem_id = -1;
return false;
}
}
return -1 != sem_id;
}
具体检查方式就是检查信号量的创建时间和预期是否一致。信号量创建时间和ID是一起保存的。
创建时间可以用来确认信号量确实是自己的。
bool _GetCTime(time_t& ctime)const
{
semid_ds ds;
union semun sem;
int ret;
memset(&ds, 0, sizeof(semid_ds));
sem.buf = &ds;
if ((ret = semctl(sem_id, 0, IPC_STAT, sem)) < 0)
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << "获得sem " << sem_id << " 状态出错(" << ret << "):" << errno2str();
return false;
}
ctime = ds.sem_ctime;
return true;
}
bool _RLock(bool no_wait)const
{
constexpr short int flag = SEM_UNDO;
constexpr short int flag_nowait = SEM_UNDO | IPC_NOWAIT;
struct sembuf sops_wait[] = { {1,0,flag},{2,1,flag},{3,0,flag} };
struct sembuf sops_nowait[] = { {1,0,flag_nowait},{2,1,flag_nowait},{3,0,flag_nowait} };
struct sembuf* sops = sops_wait;
if (no_wait)sops = sops_nowait;
if (-1 != semop(sem_id, sops, 3))
{
after_RLock();
return true;
}
else
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << errno2str();
return false;
}
}
bool _WLock(bool no_wait)const
{
STATIC_C short int const flag = SEM_UNDO;
STATIC_C short int const flag_nowait = SEM_UNDO | IPC_NOWAIT;
struct sembuf sops_wait[] = { {0,-1,flag},{1,1,flag},{2,0,flag},{3,1,flag} };
struct sembuf sops_nowait[] = { {0,-1,flag_nowait},{1,1,flag_nowait},{2,0,flag_nowait},{3,1,flag_nowait},{3,-1,flag_nowait} };
struct sembuf* sops = sops_wait;
if (no_wait)sops = sops_nowait;
//先增加写等待数,阻止读请求被激活
if (-1 == semop(sem_id, &sops[3], 1))
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << errno2str();
return false;
}
if (-1 != semop(sem_id, sops, 3))
{
after_WLock();
return true;
}
else if (no_wait && EAGAIN == errno)
{//撤销写等待
if (-1 == semop(sem_id, &sops[4], 1))
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << errno2str();
}
return false;
}
else
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << errno2str();
return false;
}
}
bool RUnLock()const
{
struct sembuf sops[] = { {2,-1,SEM_UNDO} };
if (-1 != semop(sem_id, sops, 1))
{
after_RUnLock();
return true;
}
else
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << errno2str();
return false;
}
}
bool WUnLock()const
{
struct sembuf sops[] = { {0,1,SEM_UNDO},{1,-1,SEM_UNDO},{3,-1,SEM_UNDO} };
if (-1 != semop(sem_id, sops, 3))
{
after_WUnLock();
return true;
}
else
{
*(int*)&m_errid = __LINE__;
(*(stringstream*)&m_errmsg).str("");
*(stringstream*)&m_errmsg << errno2str();
return false;
}
}
(这里是结束)