From 7051e0896a5df51c00d09f1fa1936bc5ae7a6c9e Mon Sep 17 00:00:00 2001 From: root Date: Fri, 4 Mar 2022 09:45:28 +0000 Subject: gosema ipc sysv linux --- error.go | 44 ++++++++++++++++++ go.mod | 5 +++ go.sum | 2 + lstrn.go | 12 +++++ main.go | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sysv/ftok.go | 16 +++++++ sysv/sem.go | 106 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+) create mode 100644 error.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 lstrn.go create mode 100644 main.go create mode 100644 sysv/ftok.go create mode 100644 sysv/sem.go 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 +} -- cgit v1.2.3