Go syscall은 저수준 I/O의 훌륭한 대체재입니다.
Summary
We will learn about direct system call on Go. Go에서 직접적인 system call에 관하여 학습할 것입니다. Since Go is offering strict compiler errors and rigid GC, it is much better to replace low-level calls in Pure Go. Go는 엄격한 컴파일러 오류 및 엄격한 GC를 제공하므로, Pure Go에서 저수준 호출을 대체하는 것이 훨씬 더 좋습니다. Luckily, most of the C function calls are fully reimplemented in Go, in a good and contemporary manner. 다행히도, 대부분의 C 함수 호출은 Go에서 훌륭하고 현대적인 방식으로 완전히 재구현되었습니다. Let's take a look at it. 이에 관하여 살펴보겠습니다.
System Call
System call is a direct request to the operating system. System call은 운영체제에 대한 직접적인 요청입니다. Since system is usually written in rigid, old-fashioned style since it is running right on a hardware, we need to consider that its call must deliver strict, and correct form of a request. 시스템은 하드웨어에서 직접 실행되므로 일반적으로 엄격하고 구식의 스타일로 작성되기 때문에, 그 호출이 엄격하고 정확한 형식의 요청을 전달해야 함을 고려해야 합니다. So, even if we don't need some variables, we still need to fill out the size regardless of use. 따라서, 일부 변수가 필요하지 않더라도, 사용 여부와 관계없이 크기를 채워야 합니다. Let's check with fully-working example. 완전히 작동하는 예시를 통해 확인해 보겠습니다.
Full Example
1package main
2import (
3 "fmt"
4 "syscall"
5 "unsafe"
6)
7
8type sysinfo_t struct {
9 Uptime int64
10 Loads [3]uint64
11 Totalram uint64
12 Freeram uint64
13 Sharedram uint64
14 Bufferram uint64
15 Totalswap uint64
16 Freeswap uint64
17 Procs uint16
18 Pad uint16
19 _ [4]byte
20 Totalhigh uint64
21 Freehigh uint64
22 MemUnit uint32
23 _ [4]byte
24}
25
26func main() {
27 var info sysinfo_t
28 _, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
29 if errno != 0 {
30 fmt.Println("sysinfo syscall failed:", errno)
31 return
32 }
33
34 scale := float64(1 << 16)
35 fmt.Printf("Uptime: %d seconds\n", info.Uptime)
36 fmt.Printf("Load Average: %.2f %.2f %.2f\n",
37 float64(info.Loads[0])/scale,
38 float64(info.Loads[1])/scale,
39 float64(info.Loads[2])/scale)
40 fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
41 info.Totalram*uint64(info.MemUnit)/1024/1024,
42 info.Freeram*uint64(info.MemUnit)/1024/1024,
43 info.Bufferram*uint64(info.MemUnit)/1024/1024)
44 fmt.Printf("Swap: total=%d MB free=%d MB\n",
45 info.Totalswap*uint64(info.MemUnit)/1024/1024,
46 info.Freeswap*uint64(info.MemUnit)/1024/1024)
47 fmt.Printf("Processes: %d\n", info.Procs)
48}
This example includes all variables, and print extensive information of current system information.
이 예시는 모든 변수를 포함하며, 현재 시스템 정보에 대한 광범위한 정보를 출력합니다.
We can compare this code, as a locker and a key.
우리는 이 코드를 사물함과 열쇠에 비유할 수 있습니다.syscall.SYS_SYSINFO is a key that unlocks a locker that is inside of a kernel.syscall.SYS_SYSINFO는 커널 내부에 있는 사물함을 여는 열쇠입니다.
Therefore, using correct key for a locker is important.
그러므로, 사물함에 맞는 올바른 열쇠를 사용하는 것이 중요합니다.
What will happen when we use syscall.SYS_GETPID for this call?
이 호출에 syscall.SYS_GETPID를 사용하면 어떻게 될까요?
This is a key for a locker that contains Process ID.
이것은 Process ID를 포함하는 사물함의 열쇠입니다.
This will attempt to get a PID from a space for system information.
이것은 시스템 정보를 위한 공간에서 PID를 얻으려고 시도할 것입니다.
As a result, none of the information can be read correctly; the call must be retured as failed state.
그 결과, 정보가 올바르게 읽혀지지 않을 것이며, 호출은 실패 상태로 반환되어야 합니다.
Now, we need to know which items are contained, and how items are ordered. 이제 어떤 항목들이 포함되어 있는지, 그리고 항목들이 어떻게 정렬되어 있는지 알아야 합니다. In a first slot of a locker, we have Uptime, with a size of 2^64. 사물함의 첫 번째 슬롯에는 크기가 2^64인 Uptime이 있습니다. If we try to read this with 2^32, the bit sequence is not fully read. 이를 2^32로 읽으려고 시도하면, 비트 시퀀스가 완전히 읽혀지지 않습니다. We cannot use these kinds of partial binaries unless we are going to write low-level tricks. 저수준 트릭을 작성하려는 경우가 아니라면 이러한 종류의 부분적인 바이너리를 사용할 수 없습니다.
After reading 64 bits of binary data, finally we are on a second slot. 64비트의 바이너리 데이터를 읽은 후에야, 마침내 두 번째 슬롯에 도달합니다. It can only be read accurately when we have read previous 64-bit sized integer. 이는 이전 64비트 크기의 정수를 읽었을 때에만 정확하게 읽을 수 있습니다.
With repeating those strict, and logical flows to obtain a proper information from a system, we can properly handle read data. 시스템으로부터 적절한 정보를 얻기 위해 이러한 엄격하고 논리적인 흐름을 반복함으로써, 우리는 읽은 데이터를 적절하게 처리할 수 있습니다.
How to skip 'variable names'
Even though we cannot 'skip' variables themselves, it is important to distinguish used variables and discarded ones. 변수 자체를 '건너뛸' 수는 없지만, 사용되는 변수와 버려지는 변수를 구별하는 것이 중요합니다. If use of the program is clear enough, it is better to use nameless variables as placeholders than labeling each values even if they are not used forever. 프로그램의 용도가 충분히 명확하다면, 영원히 사용되지 않을 값이라도 각각 레이블을 지정하는 것보다 이름 없는 변수를 플레이스홀더로 사용하는 것이 좋습니다. Let's check this with an example, "Free Memory Checker" "Free Memory Checker"라는 예시를 통해 이를 확인해 보겠습니다.
Example - Free Memory Checker
When checking Free Memory/Swaps, we don't need other information that is indicating different resources. Free Memory/Swaps를 확인할 때, 다른 리소스를 나타내는 다른 정보는 필요하지 않습니다. To achieve a better visibility, you can make anonymous variables to hold specific spaces. 더 나은 가독성을 얻기 위해, 특정 공간을 보유하도록 익명 변수를 만들 수 있습니다.
1package main
2
3import (
4 "fmt"
5 "syscall"
6 "unsafe"
7)
8
9type sysinfo_t struct {
10 _ int64
11 _ [3]uint64
12 Totalram uint64
13 Freeram uint64
14 Sharedram uint64
15 Bufferram uint64
16 Totalswap uint64
17 Freeswap uint64
18 _ uint16 // anonymous, and unused ones are marked as _ (익명이며 사용되지 않는 것은 _로 표시됩니다)
19 _ uint16
20 _ [4]byte
21 _ uint64
22 _ uint64
23 MemUnit uint32
24 _ [4]byte
25}
26
27func main() {
28 var info sysinfo_t
29 _, _, errno := syscall.Syscall(syscall.SYS_SYSINFO, uintptr(unsafe.Pointer(&info)), 0, 0)
30 if errno != 0 {
31 fmt.Println("sysinfo syscall failed:", errno)
32 return
33 }
34
35 fmt.Printf("Memory: total=%d MB free=%d MB buffer=%d MB\n",
36 info.Totalram*uint64(info.MemUnit)/1024/1024,
37 info.Freeram*uint64(info.MemUnit)/1024/1024,
38 info.Bufferram*uint64(info.MemUnit)/1024/1024)
39 fmt.Printf("Swap: total=%d MB free=%d MB\n",
40 info.Totalswap*uint64(info.MemUnit)/1024/1024,
41 info.Freeswap*uint64(info.MemUnit)/1024/1024)
42}
Consequently, variables are read without labels. 결과적으로, 변수는 레이블 없이 읽힙니다. Although anonymous values are actually stored into a structure, there is no labels/legible marks on a code. 익명 값은 실제로 구조체에 저장되지만, 코드상에는 레이블이나 식별 가능한 표시가 없습니다.
Conclusion
- Using Go's
syscallandunsafeis still safer than C/CGo - Go의
syscall및unsafe를 사용하는 것은 여전히 C/CGo보다 안전합니다. - If you are writing a huge project that can be expaneded easily:
- 쉽게 확장될 수 있는 대규모 프로젝트를 작성하는 경우:
- Don't make anonymous variables; make each names for the members.
- 익명 변수를 만들지 말고, 멤버마다 이름을 지정하십시오.
- If you are writing a project that has limited use:
- 사용이 제한적인 프로젝트를 작성하는 경우:
- You can use anonymous variables to hold spaces those are actually unused.
- 실제로 사용되지 않는 공간을 보유하기 위해 익명 변수를 사용할 수 있습니다.
- Go's
syscallis powerful and modern to handle low-level calls - Go의
syscall은 저수준 호출을 처리하는 데 강력하고 현대적입니다.