前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Postgresql源码(91)POSIX匿名信号量初始化与使用流程总结

Postgresql源码(91)POSIX匿名信号量初始化与使用流程总结

作者头像
mingjie
发布2022-12-02 14:50:40
3931
发布2022-12-02 14:50:40
举报
文章被收录于专栏:Postgresql源码分析

总结

  • Postgresql使用匿名信号量完成进程间的一些同步操作。
  • 匿名信号量由父进程创建在mmap的共享内存内,通过血缘关系继承给子进程,子进程从共享内存中获取信号量数据结构直接使用即可。
  • Postgresql的信号量分配比较简单,每一个进程拥有一个自己的信号量。初始化后值为1,表示未锁定状态。
    • 加锁后信号量=0。
    • 解锁后信号量=1。
  • Postgresql的信号量初始化使用的是POSIX接口(SYSTEM V)中的匿名信号量(命名信号量)。
代码语言:javascript
复制
struct PGPROC
{
	...
	PGSemaphore sem;
	...         |     -------- 是否共享,如果是需要在共享内存中获取newsem
};              |    |
                v    v  v----------- 初始值:1
if (sem_init(newsem, 1, 1) < 0)
...

POSIX相比SYSTEM V接口的优势:

  • POSIX信号量接口与System V信号量接口相比要简单许多。
  • 将一个 POSIX 未命名信号量与动态分配的内存对象关联起来更加简单:只需要将信号量嵌入到对象中即可。
  • 在高度频繁地争夺信号量的场景中,POSIX 信号量的性能与 System V 信号量的性能是类似的。但在争夺信号量不那么频繁的场景中(即信号量的 值能够让操作正常执行而不会阻塞操作)POSIX 信号量的性能要比 System V 信号量好很多。POSIX 在这种场景中之所以能够做得更好是因为它们的实现方式只有在发生争夺的时候才需要执行系统调用,而 System V 信号量操作则不管是否发生争夺都 需要执行系统调用。

POSIX相对SYSTEM V接口的劣势:

  • POSIX信号量的可移植性稍差。(Linux直到内核2.6才开始支持命名信号量)
  • POSIX信号量不支持SystemV信号量中的撤销特性。(没啥用)

仿照Postgresql使用实例

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>

#define PG_CACHE_LINE_SIZE 128
#define PG_SEM_REF(x) (&(x)->sem_padded.pgsem)

typedef union SemTPadded
{
	sem_t pgsem;
	char pad[PG_CACHE_LINE_SIZE];
} SemTPadded;

typedef struct PGSemaphoreData
{
	SemTPadded sem_padded;
} PGSemaphoreData;

typedef struct PGSemaphoreData *PGSemaphore;
static PGSemaphore sharedSemas;

int main(int argc, char *argv[])
{
	sem_t *newsem;
	int errStatus;

	sharedSemas = mmap(NULL, sizeof(PGSemaphoreData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
	if (sharedSemas == MAP_FAILED)
	{
		fprintf(stderr, "mmap() failed\n");
		exit(EXIT_FAILURE);
	}

	newsem = PG_SEM_REF(sharedSemas);
	if (sem_init(newsem, 1, 1) < 0)
	{
		fprintf(stderr, "sem_init() failed\n");
		exit(EXIT_FAILURE);
	}
	sem_wait(newsem);

	/*Parent and child share mapping*/
	switch (fork())
	{
	case -1:
		fprintf(stderr, "fork() failed\n");
		exit(EXIT_FAILURE);

	case 0:
		printf("[child]mmap address %p\n", sharedSemas);
		do
		{
			printf("[child]sem_post...\n");
			errStatus = sem_post(newsem);
		} while (errStatus < 0 && errno == EINTR);
		printf("[child]exit\n");
		exit(EXIT_SUCCESS);

	default:
		printf("[parent]mmap address %p\n", sharedSemas);
		do
		{
			printf("[parent]start semi_wait...\n");
			errStatus = sem_wait(newsem);
		} while (errStatus < 0 && errno == EINTR);
		printf("[parent]stop semi_wait...\n");
		if (wait(NULL) == -1)
		{
			fprintf(stderr, "wait() failed\n");
			exit(EXIT_FAILURE);
		}
		printf("[parent]stop waiting child exit\n");

		if (munmap(sharedSemas, sizeof(int)) == -1)
		{
			fprintf(stderr, "munmap() failed\n");
			exit(EXIT_FAILURE);
		}
		printf("[parent]exit\n");
		exit(EXIT_SUCCESS);
	}
}

// gcc -o main1 -Wall -g -ggdb -O0 -g3 -gdwarf-2 main1.c -lpthread

执行结果:

代码语言:javascript
复制
[parent]mmap address 0x7f69797d4000
[parent]start semi_wait...
[child]mmap address 0x7f69797d4000
[child]sem_post...
[child]exit
[parent]stop semi_wait...
[parent]stop waiting child exit
[parent]exit

Postgresql用到信号量的场景

组提交中充当读barrier:

  • CLOG中的TransactionGroupUpdateXidStatus
  • ProcArrayGroupClearXid

LWLock中锁队列的唤醒:

  • LWLockDequeueSelf
  • LWLockAcquire
  • LWLockAcquireOrWait
  • LWLockWaitForVar

轻量锁是自带所队列的,等锁的进程会按顺序唤醒,等锁的进程都是等在信号量上了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结
  • 仿照Postgresql使用实例
  • Postgresql用到信号量的场景
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档