The practice of system invocation in Golang-syscall. Mmap ()

Source: Internet
Author: User

While looking at some of the basic tools implemented in other languages, we sometimes find that there is a special feature we need. The source code, will generally see two kinds of bottom-level implementation: assembly, System calls. The system call here is the protagonist of our day.

System calls

Image.png

System call occupies an important position in the operating system, is the portal of external interaction of the kernel, provides us with the relatively simple and safe way to interact with the underlying resources, and provides us with a means of switching between the user state and the kernel state.

The program we write is usually run in the user state, which corresponds to the Ring 3 protection level of the CPU, while the kernel runs at the ring level 0 and has higher privileges. Accordingly, the kernel code can run some CPU privilege instructions that the user-state code cannot run, and do something that the user-state code cannot do, such as: control the running of the process, and use the hardware on the machine to drive the operation. The kernel encapsulates some of its own implementation, creating a relatively uniform and convenient interface that is called by the system.

In general, we use some special instructions to notify the kernel to execute the corresponding code for these system calls, such as Int 0x80, Sysenter, Syscall. When the kernel receives these instructions, it performs the corresponding functions according to the parameters given by our process. At this point, our process will also switch from the user state to the kernel state.

The realization of syscall in Golang

Open the Godoc syscall package in the document, you can see the standard library to these system calls do a good package, a lot of common system calls can be like normal functions directly call, in addition, also provides 4 general package, for us to execute arbitrary system calls:

Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)

From the visual observation, you can know that they can be divided into two categories by the number of supported parameters:

    • Used for system calls of 4 and 4 of the following parameters Syscall ,RawSyscall
    • Used for system calls of 6 and 6 of the following parameters Syscall6 ,RawSyscall6

And from the perspective of more meaningful realization and function, we can be divided into Syscall RawSyscall two categories.

Syscall

Needless to say, let's take a look Syscall at the specific implementation:

// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);// Trap # in AX, args in DI SI DX R10 R8 R9, return in AX DX// Note that this differs from "standard" ABI convention, which// would pass 4th arg in CX, not R10.TEXT    ·Syscall(SB),NOSPLIT,$0-56    CALL    runtime·entersyscall(SB)    MOVQ    a1+8(FP), DI    MOVQ    a2+16(FP), SI    MOVQ    a3+24(FP), DX    MOVQ    $0, R10    MOVQ    $0, R8    MOVQ    $0, R9    MOVQ    trap+0(FP), AX  // syscall entry    SYSCALL    CMPQ    AX, $0xfffffffffffff001    JLS ok    MOVQ    $-1, r1+32(FP)    MOVQ    $0, r2+40(FP)    NEGQ    AX    MOVQ    AX, err+48(FP)    CALL    runtime·exitsyscall(SB)    RETok:    MOVQ    AX, r1+32(FP)    MOVQ    DX, r2+40(FP)    MOVQ    $0, err+48(FP)    CALL    runtime·exitsyscall(SB)    RET</pre>

In this compilation, a total of 6 steps are implemented:

    1. Call the runtime.entersyscall function. Notify runtime Scheduler to let run time
    2. Read the memory and put each parameter in the appropriate register
    3. Notifies the kernel to perform system calls
    4. Determine the execution result of the system call and jump
    5. If the execution succeeds, the copy executes the result to the return value. If execution fails, the null return value
    6. Call the runtime.exitsyscall function to restore the run of the Goroutine

Rawsyscall

RawSyscallThe assembly is implemented with Syscall consistency, the only difference being that there is no invocation runtime.entersyscall and runtime.exitsyscall , in other words, the direct use RawSyscall of possible blocking situations.

When it comes to blocking, you have to explain that there are two types of system calls: Fast system calls, slow system calls. Fast system tuning refers to a system call that does not cause blocking, such as obtaining a PID. Correspondingly, a slow system refers to a system call that can cause blocking, such as reading and writing disks and networks. Although it may be felt that these slow system calls are also executed quickly, but they are slower than the CPU, in some cases, this speed will be slowed down a lot, even in the case of suspended animation (hang).

So, as discussed in the Golang mailing list, unless you know a lot about the specific system calls you want to use, and the performance requirements are extremely high, don't use other scenarios RawSyscall .

I would say that Go programs should always call Syscall. Rawsyscall exists to make it slightly more efficient to call system calls that never block, such as Getpid. But it ' s really ann internal mechanism.

Generation of Syscall Libraries

Observe the Syscall library source file distribution, you can see in addition to a bunch .s of suffix named, the file, .go there are some suffixes named .sh , .pl the file, these are the Syscall Library part of the package code of the automatic generation script.

Browse these files to know that the Syscall encapsulation in Golang is automatic, the main way is to use gcc the/usr/include/x86_64-linux-gnu/asm/unistd_64.h for processing, Then the processing results are replaced by text to generate platform-related source code files.

Execute system call

With a basic understanding, we can make some attempts, before trying to affirm that the system call is a strong correlation with the operating system, different platforms use different ways, the description is only linux amd64 valid under the platform. At the same time, improper use of the system calls may cause some abnormal behavior of the operating system, which needs to read the specific description of the corresponding system call before use.

Common system Calls

Golang's Syscall library has encapsulated the usual system calls, and we just need to invoke the corresponding function and pass in the corresponding parameters to wait for the execution to complete and return the desired result.

Wait, here we need to pass in the corresponding parameters, there are more than one return value, how to fill these parameters, the return value of what is the meaning of it? Unfortunately, the Syscall library does not have a necessary introduction to these content, which means we need to find a profile on our own, providing a relatively authoritative description of each system invocation in detail.

manThe commands we often use on weekdays, in addition to the use of various commands, provide a lot of system-level information, including specific descriptions of the various system calls we need. Through the comparison man of the data and the appearance of the package function, we can get the specific system calls the corresponding practice mode.

In addition to the command line we can directly use the man command for offline access, but also in the man7.org online query, easy to use in the development, operation of the environment in different situations.

Mmap

More useless, we have to do a try, in the realization of the process to understand the specific practical way. Here, we choose mmap to use persisted storage for data as an example.

First we need to look at the information, to mmap have a basic understanding of it to map files into the basic principles of memory, as well as compared to the traditional file read and write methods of advantages and disadvantages.

Then, look at the encapsulation of the standard library for mmap this system call:

func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error)

Next, we look man at the introduction to Mmap:

void *mmap(void *addr, size_t length, int prot, int flags,                  int fd, off_t offset);

Here, the two sides will be able to correspond, let us look at the individual parameters of the specific definition:

    • FD: File descriptor mapped into memory
    • Offset: The starting position of the file segment mapped into memory, offset in the file
    • Length: Size of the file segment mapped into memory, must be a positive integer
    • prot:protection abbreviation for permission control, Golang Standard library already has predefined values
    • Flags: Some of the mmap behaviors are controlled, Golang standard libraries already have predefined values

Again, the return value can correspond, but there are some forms of transformation that need to be understood and translated:

    • Data: Corresponds *addr to the array of file segments that are mapped into memory, and the persisted data is the use of this array
    • ERR: Corresponds to the return value of the function, the void meaning of the return value, the corresponding definition in Golang

Let's try and write out the implementation code according to this document:

func main() {    f, err := os.OpenFile("mmap.bin", os.O_RDWR|os.O_CREATE, 0644)    if nil != err {        log.Fatalln(err)    }    // extend file    if _, err := f.WriteAt([]byte{byte(0)}, 1<<8); nil != err {        log.Fatalln(err)    }    data, err := syscall.Mmap(int(f.Fd()), 0, 1<<8, syscall.PROT_WRITE, syscall.MAP_SHARED)    if nil != err {        log.Fatalln(err)    }    if err := f.Close(); nil != err {        log.Fatalln(err)    }    for i, v := range []byte("hello syscall") {        data[i] = v    }    if err := syscall.Munmap(data); nil != err {        log.Fatalln(err)    }}

Compiling and executing this code will generate the file in the current directory, the mmap.bin execution hexdump -C mmap.bin can be seen, the file already has the content we write.

Any system call

The practice of executing arbitrary system calls is similar to executing common system calls. However, the system calls that are not encapsulated are generally called system calls with few scenarios, which means that there is less data to find, and man the information is not necessarily complete.

Less information does not mean that no, golang information can not find, may wish to find a C + + related practice, but also directly to see the implementation of the system calls open source project source. Even, in extreme cases, we can directly see the system calls the corresponding kernel source code. It is recommended to use https://syscalls.kernelgrok.com to quickly locate specific system calls in the kernel source of the specific location. However, these data collection methods, the knowledge of our operating system, C-language source of the reading capacity of the higher requirements.

If you find enough information, you can begin to implement it. syscall.Syscall, you can find the answer in some commonly used system call package source code:

    • The first parameter is the system call number, which is usually preceded by a SYS_ start.
    • The subsequent parameters are the man individual parameters that are written in it. It is not necessarily a pointer, it may be some number, it is uniformly passed as a uintptr type, and some cases require a forced type conversion.
    • When a system call requires a parameter less than 4 (6), the missing parameter item is filled with 0

Here gotty , the system calling source code for the number of TTY lines and columns is set as an example:

            window := struct {                row uint16                col uint16                x   uint16                y   uint16            }{                rows,                columns,                0,                0,            }            syscall.Syscall(                syscall.SYS_IOCTL, // syscall number                context.pty.Fd(),                syscall.TIOCSWINSZ, // call option                uintptr(unsafe.Pointer(&window)),            )

Do you want to use system call

As described at the beginning of this article, system calls can interact directly with the kernel, which is undoubtedly more efficient than using shell commands to interact with the kernel. If the standard library has already encapsulated the system call, the direct use of the corresponding encapsulation is shell more obvious than the advantage of using the command's interactive approach.

Therefore, when the code is to implement a function, and our code to run as a long-running stable service, we should try to use system calls, rather than executing commands in the source code implementation shell . Conversely, if you just write some temporary, less efficient tools, which is convenient to use which.

It is important to note that third-party tools are invoked in our source code, and we are responsible for the correctness of these third-party tools. One is to ensure that the right way to use, the second is in the internal implementation of third-party tools have bugs, we have the ability to analyze and diagnose the corresponding problems.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.