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


    总结

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

    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使用实例

    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    执行结果:

    [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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Postgresql用到信号量的场景

    组提交中充当读barrier:

    • CLOG中的TransactionGroupUpdateXidStatus
    • ProcArrayGroupClearXid

    LWLock中锁队列的唤醒:

    • LWLockDequeueSelf
    • LWLockAcquire
    • LWLockAcquireOrWait
    • LWLockWaitForVar

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

  • 相关阅读:
    jmeter之跨线程关联
    二分查找(搜索区间为左闭右开)
    数据结构:栈
    项目管理之如何高效召开项目团队会议
    Untiy 一个有意思的透视Shader
    mysql启动报错:ERROR! The server quit without updating PID file
    C#面试题 1
    关于python函数,你该了解这些
    加强版python连接飞书通知——本地电脑PC端通过网页链接打开本地已安装软件(调用注册表形式,以漏洞扫描工具AppScan为例)
    Redis为什么变慢了
  • 原文地址:https://blog.csdn.net/jackgo73/article/details/128070802