© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
N. TolaramSoftware Development with Gohttps://doi.org/10.1007/978-1-4842-8731-6_1

1. System Calls

Nanik Tolaram1  
(1)
Sydney, NSW, Australia
 

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/.

Source Code

The source code for this chapter is available from the https://github.com/Apress/Software-Development-Go repository.

What Is a System Call?

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 ((host = gethostbyname(hostname)) == NULL) {
     return -1;
   }
   memcpy( & server.sin_addr, host -> h_addr, host -> h_length);
 }
 server.sin_family = AF_INET;
 server.sin_port = htons(80);
 if (connect(socket_desc, (struct sockaddr * ) & server, sizeof(server)) < 0) {
   puts("connect error");
   return 1;
 }
 puts("Connected ");
 //Send some data
 message = "GET / HTTP/1.0 ";
 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 gethostbyname function. 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/unix package 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 Getcwd function.

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:
go run statx.go ./README.md
You will see output as follows:
  File: ./README.md
  Size: 476                Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d      Inode:  2637168   Links:    1
Access: (0644/-rw-r--r--) Uid:    (1000/     nanik)   Gid: (1000/   nanik)
Access: 2022-02-19 18:10:29.919351223 +1100 AEDT
Modify: 2022-02-19 18:10:29.919351223 +1100 AEDT
Change: 2022-02-19 18:10:29.919351223 +1100 AEDT
 Birth: 2022-02-19 18:10:29.919351223 +1100 AEDT
 Attrs: 0000000000000000 (-----....)
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.
import (
   ....
   "golang.org/x/sys/unix"
)
   ....
func main() {
   log.SetFlags(0)
   flag.Parse()
   if len(flag.Args()) < 1 {
       flag.Usage()
       os.Exit(1)
   }
   ....
   for _, arg := range flag.Args() {
       var statx unix.Statx_t
       if err := unix.Statx(unix.AT_FDCWD, arg, flags, mask, &statx); err != nil {
   ....
       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:
func Statx(dirfd int, path string, flags int, mask int,
stat *Statx_t) (err error)
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.Statx function, 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/unix package. 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.167.178