Linux provides a lot of features and provides applications access to everything that the operating system has access to. When discussing system calls, most people will turn their attention to using C because it is the most common language to use when interfacing with the operating system.
In this chapter, you will explore what system calls are and how you can program in Go to make system calls. By the end of this chapter, you will have learned the following:
What a system call looks like in C
Understanding the sys/unix Go package
Exploring a project using system calls
If you are using Go for the first time, refer to the online documentation at https://go.dev/doc/install. The online documentation will walk you through the steps to install Go on your local computer. Go through the Go tutorial that the Go documentation provides at https://go.dev/doc/.
A system call is the interface provided by the underlying operating system that your application is currently running on. Using this interface, your application can communicate with the operating system to perform an operation. In general, the operating system provides numerous services that applications can take advantage of.
Figure 1-1 shows at a high level how an application uses system calls to request some service operation to the operating system. The user app will make a call to the provided system library, which in this case is the Go library, and it will call the operating system service through the provided interface. Data transfer flows in both directions for the different components.
A block diagram of the Linux operating system includes the user app being connected to the system library in the user space that gives service in the kernel space.
Figure 1-1
High-level view of a system call
Operating systems provide a large number of system calls that applications can use. Figure 1-2 shows a snapshot list of system calls. For a complete available Linux system call list, you can visit https://man7.org/linux/man-pages/man2/syscalls.2.html.
A screenshot lists down system calls available at kernel 5.11 in 3 columns as system call, kernel, and its notes. There are a total of 13 kernel versions.
Figure 1-2
Snapshot of a Linux system call
C System Call
In this section, you will briefly look at how system calls normally work inside a C program. This will give you an idea of how system calls are done in C compared to how they are done in Go.
You will see a simple example of using a socket to connect to a server and read the response. The code can be found inside the chapter1/c directory. The code creates a socket and uses it to connect to a public website named httpbin.org and print the response it receives to the screen. Listing 1-1 shows the sample code.
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netdb.h>
int main(int argc, char * argv[]) {
int socket_desc;
struct sockaddr_in server;
char * message, server_reply[2000];
struct hostent * host;
const char * hostname = "httpbin.org";
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
printf("Could not create socket");
}
if ((server.sin_addr.s_addr = inet_addr(hostname)) == 0xffffffff) {
if (send(socket_desc, message, strlen(message), 0) < 0) {
puts("Send failed");
return 1;
}
puts("Data Send
");
//Receive a reply from the server
if (recv(socket_desc, server_reply, 2000, 0) < 0) {
puts("recv failed");
}
puts("Reply received
");
puts(server_reply);
return 0;
}
Listing 1-1
Sample Code
To test the code, make sure you have a C compiler installed in your machine. Follow the instructions outlined on the GCC website to install the compiler and tools (https://gcc.gnu.org/). Use the following command to compile the code:
cc sample.c -o sample
The code will be compiled to an executable named sample, and it can be run by just typing ./sample on the command line. After a successful run, it will print out the following:
Connected
Data Send
Reply received
HTTP/1.1 200 OK
Date: Tue, 01 Mar 2022 10:21:13 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9593
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
The code sample shows the system call that it uses to resolve the address of httpbin.org to an IP address by using the gethostbynamefunction. It also uses the connect function to use the newly created socket to connect to the server.
In the next section, you will start exploring Go by using the standard library to write code using system calls.
sys/unix Package
The sys/unixpackage is a package provided by the Go language that provides a system-level interface to interact with the operating system. Go can run on a variety of operating systems, which means that it provides different interfaces to applications for different operating systems. Complete package documentation can be found at https://pkg.go.dev/golang.org/x/sys/unix. Figure 1-3 shows different system calls in different operating systems, in this case between Darwin and Linux.
A screenshot compares a code between two users named syscall slash darwin and 64-bit and syscall slash Linux and 64-bit in 48 rows.
Figure 1-3
System calls in Linux vs. Darwin
Listing 1-2 shows how to use system calls using the sys/unix package.
package main
import (
u "golang.org/x/sys/unix"
"log"
)
func main() {
c := make([]byte, 512)
log.Println("Getpid : ", u.Getpid())
log.Println("Getpgrp : ", u.Getpgrp())
log.Println("Getppid : ", u.Getppid())
log.Println("Gettid : ", u.Gettid())
_, err := u.Getcwd(c)
if err != nil {
log.Fatalln(err)
}
log.Println(string(c))
}
Listing 1-2
Go System Call
The code prints out information that it obtained by calling the following system calls:
Getpid
Obtains the process id of the current running sample app
Getpgrp
Obtains the group process id of the current running app
Getppid
Obtains the parent process id of the current running app
Gettid
Obtains the caller’s thread it
Running the app on a Linux machine will result in output something like the following:
2022/02/19 21:25:59 Getpid : 12057
2022/02/19 21:25:59 Getpgrp : 12057
2022/02/19 21:25:59 Getpgrp : 29162
2022/02/19 21:25:59 Gettid : 12057
2022/02/19 21:25:59 /home/nanik/
The other system call that the application uses is to get the current working directory using the Getcwdfunction.
System Call in Go
In the previous section, you looked at a simple example of using the sys/unix package. In this section, you will explore more on system calls by looking at an open source project. The project can be found at https://github.com/tklauser/statx. This project works similarly to the stat command in Linux for printing out statistical information about a particular file.
Change your directory to the statx project and compile and run the app as follows:
How does the application get all this information about the file? It obtains the information from the operating system by making a system call. Let's take a look at the code in Listing 1-3.
dev := unix.Mkdev(statx.Dev_major, statx.Dev_minor)
....
}
Listing 1-3
Code Using statx
As seen in the snippet, the application uses a unix.Statx system call and it passes filename and other relevant arguments. The system call is provided as part of the golang.org/x/sys/unix package, which is declared as follows:
Declaration and documentation of the Statx function system call can be found in the following link: https://pkg.go.dev/golang.org/x/sys/unix. Going through the documentation, there is not much information about the parameters. As an alternative, you can take a look at the same system call defined for Linux, which can be found at https://man7.org/linux/man-pages/man2/statx.2.html. Figure 1-4 shows information about the different parameters that the function call accepts and what they mean.
A screenshot of the function name invoking statx, statx, and the absolute pathname, a directory-relative pathname, by the file descriptor.
Figure 1-4
Linux statx
On successful return from calling the unix.Statxfunction, the application processes the information that is inside the statx variable to extract information. The variable is of type Statx_t, which is defined as follows in the sys/unix package. The struct contains a fair amount of data pertaining to the file that the application has access to. Using this information, the application will print out information such as file size, type of file, user id, and group id.
type Statx_t struct {
Mask uint32
Blksize uint32
Attributes uint64
Nlink uint32
Uid uint32
Gid uint32
Mode uint16
_ [1]uint16
Ino uint64
Blocks uint64
Attributes_mask uint64
Atime StatxTimestamp
...
Dev_major uint32
Dev_minor uint32
...
}
Summary
In this chapter, you learned what system calls are and how to write a simple application to interface with the operating system by using the sys/unixpackage. You dug deeper into system calls by looking at an open source project to learn how it uses the system calls to provide statistical information about a particular file.
In the next chapters, you will explore system calls more and you will look at various ways to interface with the operating system using Go.