文章目录
go
本身提供的
semaphore
只能在同一个进程多个协程或线程间使用,无法在不同的
go
进程之间使用,所以本文介绍,如何使用
go
中的
syscall
来使用
POSIX
系统提供的
命名信号量
。
本文涉及到的代码可在此仓库查看:semaphore。
Go 中的系统调用
在go
中,系统调用是通过syscall
包提供的Syscall
函数来进行系统调用的,不同的系统调用有不同的trap
,以及不同长度的参数
。
trap
go
在syscall
包中定义了大量的系统调用码,具体定义在文件1.20.6/go/src/syscall/zsysnum_darwin_arm64.go
。不同操作系统上,定义所使用的文件是不同的,这些定义都是通过不同系统的c 语言头文件自动生成的。比如linux amd64
操作系统的定义在1.20.6/go/src/syscall/zerrors_linux_amd64.go
。
不同长度的参数
syscall
包有Syscall、Syscall6
两个函数,对应于不同的操作系统调用参数长度的情况。
Syscall
总共接收4
个参数,第一个是trap
定义,描述具体的系统调用,剩下的3
个是系统调用所需的参数。
Syscall6
总共接收7
个参数,第一个是trap
定义,描述具体的系统调用,剩下的6
个是系统调用所需的参数。
如果使用Syscall
或Syscall6
时,系统调用所需的参数不满足函数形参所需的数量,则剩下的参数传0
。
例如,在POSIX
系统上打开一个命名信号量的系统调用是:
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
因为系统调用的参数有4
个,而Syscall
接收的全部形参才4
个,所以Syscall
不能满足我们的需求,只能使用Syscall6
这个函数。而Syscall6
总共需要7
个形参,其中有6
个是系统调用参数,我们只有4
个系统调用参数,那么剩下的2
个系统调用参数,我们就可以使用0
替代,例如:
r1, r2, err := syscall.Syscall6(
syscall.SYS_SEM_OPEN,
uintptr(unsafe.Pointer(cs)), // name
uintptr(C.O_CREAT), // flag
uintptr(mode), // mode
uintptr(value), // value
0, // 没有更多参数,使用 0
0,
)
实现Samephore
信号量的操作主要有这么几个系统调用:sem_open、sem_wait、sem_trywait、sem_post、sem_close、sem_unlink
。
在具体实现之前,我们先引入C
头文件和定义一些结构,方便我们后续使用。
我们可以创建一个 semaphore/semaphore.go
的包ÿ