aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorroot <sina@snix.ir>2022-03-04 09:45:28 +0000
committerroot <sina@snix.ir>2022-03-04 09:45:28 +0000
commit7051e0896a5df51c00d09f1fa1936bc5ae7a6c9e (patch)
treef8ac703611469f9190de87a2d698288fcf9f2c8e
gosema ipc sysv linux
-rw-r--r--error.go44
-rw-r--r--go.mod5
-rw-r--r--go.sum2
-rw-r--r--lstrn.go12
-rw-r--r--main.go143
-rw-r--r--sysv/ftok.go16
-rw-r--r--sysv/sem.go106
7 files changed, 328 insertions, 0 deletions
diff --git a/error.go b/error.go
new file mode 100644
index 0000000..01dcc2d
--- /dev/null
+++ b/error.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+const (
+ format = "\033[1;31mfatal --\033[0m %s\n"
+ infomt = "\033[1;32mcmlog --\033[0m"
+ warnmt = "\033[1;33mwarng --\033[0m"
+)
+
+type mainerr error
+
+func defexit() {
+ r := recover()
+ switch x := r.(type) {
+ case mainerr:
+ fmt.Printf(format, x.Error())
+ os.Exit(-1)
+ case nil:
+ os.Exit(0)
+ default:
+ panic(x)
+ }
+}
+
+func fatal(err error) { panic(mainerr(err)) }
+func exits() { fatal(nil) }
+func logst(d ...interface{}) {
+ d = append([]interface{}{infomt}, d...)
+ fmt.Println(d...)
+}
+
+func warng(d ...interface{}) {
+ d = append([]interface{}{warnmt}, d...)
+ fmt.Println(d...)
+}
+
+func usage(st string) {
+ fmt.Printf("usage: %v [client|server] .. \n", st)
+ exits()
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..2abf049
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module gosema
+
+go 1.17
+
+require golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..768bd3f
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
+golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/lstrn.go b/lstrn.go
new file mode 100644
index 0000000..2b7aa29
--- /dev/null
+++ b/lstrn.go
@@ -0,0 +1,12 @@
+package main
+
+import "errors"
+
+var (
+ errNotSame = errors.New("shared memory size is not same as our request")
+)
+
+const (
+ client = "client"
+ server = "server"
+)
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..4300ae4
--- /dev/null
+++ b/main.go
@@ -0,0 +1,143 @@
+package main
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "gosema/sysv"
+ "os"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+/*
+
+ an example of ipc sysv with golang - this can be done with newer apis
+ like shm_open(), /dev/shm or mmap() too, sysv is the oldest way to do
+ this, and it's commonly used.
+ author: sina@snix.ir
+
+*/
+
+const (
+ //SHMSEMFLG = 0666 | unix.IPC_CREAT | unix.IPC_EXCL
+ SHMSEMFLG = 0666 | unix.IPC_CREAT // open shm and sem with this flag
+ SEMSHEMID = 0x12 // shm and sem id
+ NUMSOFSEM = 0x01 // number of sems
+ SHRDMEMRY = 0x20 // size of shared memory (32 byte)
+)
+
+const SEM_A = 0x00
+
+func clntOrServ() bool {
+ if os.Args[1] == server {
+ return true
+ }
+ if os.Args[1] != client {
+ usage(os.Args[0])
+ }
+ return false
+}
+
+func main() {
+ defer defexit()
+
+ if len(os.Args) < 2 {
+ usage(os.Args[0])
+ }
+
+ serv := clntOrServ()
+
+ semkey, err := sysv.Ftok(os.DevNull, SEMSHEMID)
+ if err != nil {
+ fatal(err)
+ }
+
+ shmkey, err := sysv.Ftok(os.DevNull, SEMSHEMID)
+ if err != nil {
+ fatal(err)
+ }
+
+ shmidd, err := unix.SysvShmGet(int(shmkey), SHRDMEMRY, SHMSEMFLG)
+ if err != nil {
+ fatal(err)
+ }
+
+ defer unix.SysvShmCtl(shmidd, unix.IPC_RMID, &unix.SysvShmDesc{})
+
+ mem, err := unix.SysvShmAttach(shmidd, 0, 0)
+ if err != nil {
+ fatal(err)
+ }
+
+ defer unix.SysvShmDetach(mem)
+
+ if len(mem) != SHRDMEMRY {
+ fatal(errNotSame)
+ }
+
+ logst("shared memory is now ready to use", SHRDMEMRY)
+
+ sempht, err := sysv.NewSemGet(int(semkey), NUMSOFSEM, SHMSEMFLG)
+ if err != nil {
+ fatal(err)
+ }
+
+ defer sempht.DelSem()
+
+ // set semval to 1, which force client to wait for semaphore
+
+ switch {
+ case serv:
+ if err := sempht.SetVal(SEM_A, 1); err != nil {
+ warng(err)
+ }
+
+ for {
+
+ l, _ := sempht.GetVal(SEM_A) // value of semaphore
+ s, _ := sempht.GetPID(SEM_A) // pid that holds semaphore
+ fmt.Printf("sem locked by process pid: %d, semval: %d\n", s, l)
+
+ // lock sem[0], make semval = semval-1 and if semval is 0 then no one
+ // can lock semaphore any more
+ if err := sempht.Lock(SEM_A); err != nil {
+ warng(err)
+ continue
+ }
+
+ // l, _ = sempht.GetVal(SEM_A)
+ // fmt.Println(l)
+
+ // write some dummy random data to shared mem
+ if err := makeRand(mem); err != nil {
+ warng(err)
+ }
+
+ time.Sleep(time.Second) // every one sec
+ fmt.Println("i just put some random jokes in memory.. yeay")
+ if err := sempht.Unlock(SEM_A); err != nil {
+ warng(err)
+ } //unlock sem[0], make semval = semval+1
+ }
+ }
+
+ for {
+ if err := sempht.Lock(SEM_A); err != nil {
+ warng(err)
+ continue // do not unlock it if you haven't locked it
+ }
+
+ fmt.Printf("data: %s\n", hex.EncodeToString(mem))
+ if err := sempht.Unlock(SEM_A); err != nil {
+ warng(err)
+ }
+ }
+}
+
+func makeRand(b []byte) error {
+ _, err := rand.Read(b)
+ return err
+
+}
diff --git a/sysv/ftok.go b/sysv/ftok.go
new file mode 100644
index 0000000..94d73cf
--- /dev/null
+++ b/sysv/ftok.go
@@ -0,0 +1,16 @@
+package sysv
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+func Ftok(p string, id uint) (uint, error) {
+ fss := &unix.Stat_t{}
+ if err := unix.Stat(p, fss); err != nil {
+ return 0, err
+ }
+
+ return uint((uint(fss.Ino) & 0xffff) |
+ uint((fss.Dev&0xff)<<16) |
+ ((id & 0xff) << 24)), nil
+}
diff --git a/sysv/sem.go b/sysv/sem.go
new file mode 100644
index 0000000..631a16b
--- /dev/null
+++ b/sysv/sem.go
@@ -0,0 +1,106 @@
+package sysv
+
+import (
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ SYS_GETPID int = 0x0B + iota
+ SYS_GETVAL
+ SYS_SETVAL int = 0x010
+)
+
+const (
+ non = iota - one
+ one = 0x01
+ zro = 0x00
+)
+
+type oprations struct {
+ num uint16
+ opt int16
+ flg int16
+}
+
+type Semaphore struct {
+ nums int
+ smid int
+}
+
+func NewSemGet(key, nums, flags int) (*Semaphore, error) {
+ id, _, errno := unix.Syscall(
+ unix.SYS_SEMGET, uintptr(key),
+ uintptr(nums), uintptr(flags),
+ )
+ if errno != 0 {
+ return nil, error(errno)
+ }
+ return &Semaphore{nums: nums, smid: int(id)}, nil
+}
+
+func (sem *Semaphore) GetVal(semnum int) (int, error) {
+ vl, _, errno := unix.Syscall(
+ unix.SYS_SEMCTL, uintptr(sem.smid),
+ uintptr(semnum), uintptr(SYS_GETVAL),
+ )
+ if errno != 0 {
+ return int(vl), error(errno)
+ }
+ return int(vl), nil
+}
+
+func (sem *Semaphore) SetVal(semnum, v int) error {
+ _, _, errno := unix.Syscall6(
+ unix.SYS_SEMCTL, uintptr(sem.smid),
+ uintptr(semnum), uintptr(SYS_SETVAL), uintptr(v), 0, 0,
+ )
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func (sem *Semaphore) GetPID(semnum int) (int, error) {
+ pid, _, errno := unix.Syscall(
+ unix.SYS_SEMCTL, uintptr(sem.smid),
+ uintptr(semnum), uintptr(SYS_GETPID),
+ )
+ if errno != 0 {
+ return int(pid), error(errno)
+ }
+ return int(pid), nil
+}
+
+func (sem *Semaphore) setops(n uint16, o, f int16) error {
+ opt := oprations{num: n, opt: o, flg: f}
+ _, _, errno := unix.Syscall(
+ unix.SYS_SEMOP, uintptr(sem.smid),
+ uintptr(unsafe.Pointer(&opt)), uintptr(sem.nums),
+ )
+
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}
+
+func (sem *Semaphore) Lock(semnum int) error {
+ return sem.setops(uint16(semnum), non, zro)
+}
+
+func (sem *Semaphore) Unlock(semnum int) error {
+ return sem.setops(uint16(semnum), one, zro)
+}
+
+func (sem *Semaphore) DelSem() error {
+ _, _, errno := unix.Syscall(
+ unix.SYS_SEMCTL, uintptr(sem.smid),
+ uintptr(0), uintptr(unix.IPC_RMID),
+ )
+ if errno != 0 {
+ return error(errno)
+ }
+ return nil
+}

Snix LLC Git Repository Holder Copyright(C) 2022 All Rights Reserved Email To Snix.IR