This is the last section of a four-part tutorial series on system programming in Golang. If you haven’t checked out the previous sections, check them out here:
Here we’ll just have a discussion on popular conceptual questions asked in this domain.
Can we use Go to write device drivers?
Go, at its core, is a cloud software language. Originally there was talk it could be a “systems” language, like C, C++, Rust, or D. This has not been the focus of Go’s creators, and most major Go-written projects are focused on cloud software and/or higher-level components in a stack. Notable “systems” software written in Go includes Docker, which is a pretty high-level piece of software. Docker does low-level things by directly touching facilities like /proc in Linux, or calling commonly available system calls. If this is your definition of “low level” then Go might be a great choice. If by lower-level you mean kernel space code or code were squeezing every ounce of performance out of the hardware is highly desirable, Go is probably not your choice.
Here are a number of reasons/hurdles Go could not be used to write a device driver:
- All compiled programs written in Go, include the Go runtime engine, and can’t live without it. That engine depends on system calls and OS facilities not available in kernel space. This pretty much prevents any device drivers from being written.
- Go does not use or directly support a C calling convention, or any calling convention besides its own. This is essentially the way parameters and return values are placed on the stack. This trait was a design decision. The decision made it easier for Go’s creators to make its runtime efficient but it has isolated the language from other system components significantly. However, Go supports calling syscalls directly to the OS, via the syscall.Syscall functions, which we’ll go over in another tutorial. However, in a driver, you no longer use syscalls – which would transition to kernel space, since you are in kernel space already.
- Go has a mandatory garbage collection. While its GC is very efficient, this is just not appropriate for a device driver. The GC must work in its own thread, it is not deterministic, and as such your driver might eventually exhibit inconsistent performance, both in CPU utilization and memory, and while minor – it might not be acceptable for a driver. A similar programming language called D allows disabling of its GC, which Go does not support.
- Go does not support raw pointers easily and its unsafe package is purposely limited. Go itself is designed to not allow this, but it allows you to do that just to be able to touch some corner cases as needed.
- The Go runtime is well built, and many pieces of software can run near C-speed. Often, since Go programs are better built, or use more parallelization, the entire software can be faster. That being said, most of the time, you don’t want your driver or “low-level” software to be highly threaded. What you want is fast and tight code – from a CPU and memory perspective. In this case, GCC, g++, or clang’s compiled code will be faster for a variety of reasons. Primarily because it does not carry runtime baggage.
However, there are always innovators. And here again, Go enthusiasts have been working on projects like https://github.com/gopher-os/gopher-os. Both are attempts at creating an OS kernel using Go. The former one is quite extensive, with 295 commits!
Will C/C++ be replaced by Go?
First, C is merely a portable assembly designed in the 70s with the programming paradigms of that time (structured programming). C design is also very close to OS kernels and system hardware. C is still very strong but it has drawbacks because both concepts and programming paradigms and hardware have evolved.
Coming to C++, it has much more. It started by adding (some kind of) Object-Oriented support to C, then template meta-programming, then tons of stuff and Concepts. Basically the idea is that C++ has it all, more than a multi-paradigm language.
But C++ does not have every feature of every other language. For instance, I believe there are important things to add for concurrency support, automated and fine-grain control memory management, or proper modules… but C++ people are trying hard to put it all and even make it efficient, and they will eventually succeed.
Also, C++ is a very complicated beast, not many people master it (while for a seasoned low-level programmer mastering C is probably a question of weeks). The C++ syntax is cumbersome. It’s quite easy to write unmaintainable code both at the syntactic and conceptual level. It’s really easy to introduce very subtle bugs. C++ is trying to have every feature from every language, and in that it’s getting complicated.
So a third player with elegant syntax and natural Concepts implementations and targeted at low-level programming and cross-language extensions is really welcome by the programming community. Now, which language do we know of that is similar? Of course, Go! So yes, there is a chance that once the Go community figures out the initial hiccups, it may be a good replacement for C++.
What about reading raw sectors, MBR, etc?
No such cross-platform API exists, at least not for Go. However, for MBR (Master Boot Record), there is a repository that contains various tools for managing disk images on Google Compute Engine.
That repository has 81.5% written in Go.
Ending Notes
This series has been an absolute blast to make. Thank you all for being such avid readers and Gophers.