Go Standard PKG - Must Know

255 阅读4分钟

记录工程中能用的到的Golang官方包以及系统调用

"os"

OpenFile

func OpenFile(name string, flag int, perm FileMode) (*File, error)

Example:

f, err = os.OpenFile(path, O_RDWR|os.O_CREATE, os.FileMode(0600))

flag:

  • O_RDWR
  • O_RDONLY
  • O_WRONLY
  • O_CREATE
  • O_APPEND

perm:

Linux/MacOS has 3 permission: r w x and has 3 identities: owner group-user other-user

image.png

The relation between number and permission is :

八進位數字權限
0沒有任何權限
1可以執行
2可以寫
3可以寫、可以執行
4可以讀
5可以讀、可以執行
6可以讀、可以寫
7可以讀、可以寫、可以執行

Getpagesize

func Getpagesize() int { return syscall.Getpagesize() }

Get memory page size

WriteAt

// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
//
// If file was opened with the O_APPEND flag, WriteAt returns an error.

func (f *File) WriteAt(b []byte, off int64) (n int, err error)

Note: WriteAt only write data into system core cache not disk, so you must call Sync to flush data into disk.

Sync

// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (f *File) Sync() error

"syscall"

Sendfile

func Sendfile(out_fd, in_fd int, offset, count int)
#include <sys/socket.h> 
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

对应JAVA是FileChannel的TransferTo

不同于mmap, sendfile本质就是在内核通过CPU拷贝将out_fd的内核数据拷贝到in_fd的内核中, 没有减少拷贝次数, 减少的是系统调用次数

Flock

func Flock(fd int, how int) (err error)

Example:

fd, _ := os.OpenFile(
flag := syscall.LOCK_NB|syscall.LOCK_EX
for {
  err := syscall.Flock(int(fd), flag) // 取锁
  time.Sleep(time.Second())
}

flag:

  1. syscall.LOCK_NB
  2. syscall.LOCK_EX
  3. syscall.LOCK_SH

"golang.org/x/sys/unix"

Mmap

// 返回的data是一个[]byte, 可以直接通过读写它, 操作系统会反应到磁盘上
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error)

prot:

  1. syscall.PROT_READ
  2. syscall.PROT_WRITE
  3. syscall.PROT_EXEC

flags:

  1. syscall.MAP_SHARED

Example:

// 只读映射PROT_READ
b, err := unix.Mmap(int(file.Fd()), 0, file.Stat().Size(), syscall.PROT_READ, syscall.MAP_SHARED|db.MmapFlags)

db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0]))

Learn More

mmap是一种内存映射文件的方法,直接把内核缓冲区里的数据「映射」到用户空间,用户进程就可以采用指针的方式读写操作这一段内核内存,而操作系统自动回写脏页到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。同时,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享

Why mmap has advantages over read/write syscall?

Linux系统下使用read系统调用的过程:

  1. 内核判断内容是否已经在页缓存中, 如果在则直接返回页缓存
  2. 如果不在先把磁盘内容读到页缓存中(以备下次使用), 再把页缓存拷贝到应用进程空间
  3. 所以总共有两次拷贝(写write也是, 先把数据从用户进程的内存拷贝到内核里的页缓存, 再由操作系统在合适的时间从内核写到磁盘)

Linux

条件变量

有这样一个场景: 我们希望一个或多个线程在共享变量x==10时再执行下面的代码

1.  void* threadFun(void * args){
1.    while(x != 10){
1.      sleep(5);
1.    }
1.  // 待条件x==10成立后,执行后续的代码
1.  }

但这样会有问题:

  1. 如果睡眠过长, 线程唤醒会有延时(比如上面可能是5秒后)
  2. 如果睡眠时间过短, for循环会一直占用CPU

这种场景下就需要条件变量pthread_cond_t

#include <pthread.h>
pthread_cond_t myCond;

// initialize
int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * attr);


// 调用这个函数前, 当前线程业务逻辑需自己完成对`mutex`的取锁加锁操作, 否则会返回失败
// 这个函数会原子性的完成“阻塞当前线程(直到接受到条件信号) + 当前线程加入条件信号等待队列并解锁`mutex`”
// 当收到条件信号时, 当前线程先尝试取锁加锁, 然后再解除条件信号阻塞
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);


// 发出解除等待队列里至少一个线程的等待状态(其他线程看操作系统调度)
int pthread_cond_signal(pthread_cond_t* cond);
// 发出解除等待队列里所有线程的等待状态
int pthread_cond_broadcast(pthread_cond_t* cond);
// 当然, 收到信号的等待状态的线程还需要实际拿到锁才能解除等待状态

"io"

blog.lfyzjck.com/2015/12/21/…

REF