记录工程中能用的到的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_RDWRO_RDONLYO_WRONLYO_CREATEO_APPEND
perm:
Linux/MacOS has 3 permission: r w x and has 3 identities: owner group-user other-user
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:
- syscall.LOCK_NB
- syscall.LOCK_EX
- 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:
- syscall.PROT_READ
- syscall.PROT_WRITE
- syscall.PROT_EXEC
flags:
- 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系统调用的过程:
- 内核判断内容是否已经在页缓存中, 如果在则直接返回页缓存
- 如果不在先把磁盘内容读到页缓存中(以备下次使用), 再把页缓存拷贝到应用进程空间
- 所以总共有两次拷贝(写
write也是, 先把数据从用户进程的内存拷贝到内核里的页缓存, 再由操作系统在合适的时间从内核写到磁盘)
Linux
条件变量
有这样一个场景: 我们希望一个或多个线程在共享变量x==10时再执行下面的代码
1. void* threadFun(void * args){
1. while(x != 10){
1. sleep(5);
1. }
1. // 待条件x==10成立后,执行后续的代码
1. }
但这样会有问题:
- 如果睡眠过长, 线程唤醒会有延时(比如上面可能是5秒后)
- 如果睡眠时间过短, 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"
REF
- perm/fileMode in Linux mileslin.github.io/2020/09/Gol…
- Talk about MMAP www.cnblogs.com/huxiao-tee/…
- Linux ptread cond c.biancheng.net/view/8633.h…
- Zero copy summary www.cnblogs.com/pam-sh/p/16…