Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Linux System Programming Techniques

You're reading from   Linux System Programming Techniques Become a proficient Linux system programmer using expert recipes and techniques

Arrow left icon
Product type Paperback
Published in May 2021
Publisher Packt
ISBN-13 9781789951288
Length 432 pages
Edition 1st Edition
Tools
Arrow right icon
Author (1):
Arrow left icon
Jack-Benny Persson Jack-Benny Persson
Author Profile Icon Jack-Benny Persson
Jack-Benny Persson
Arrow right icon
View More author details
Toc

Table of Contents (14) Chapters Close

Preface 1. Chapter 1: Getting the Necessary Tools and Writing Our First Linux Programs 2. Chapter 2: Making Your Programs Easy to Script FREE CHAPTER 3. Chapter 3: Diving Deep into C in Linux 4. Chapter 4: Handling Errors in Your Programs 5. Chapter 5: Working with File I/O and Filesystem Operations 6. Chapter 6: Spawning Processes and Using Job Control 7. Chapter 7: Using systemd to Handle Your Daemons 8. Chapter 8: Creating Shared Libraries 9. Chapter 9: Terminal I/O and Changing Terminal Behavior 10. Chapter 10: Using Different Kinds of IPC 11. Chapter 11: Using Threads in Your Programs 12. Chapter 12: Debugging Your Programs 13. Other Books You May Enjoy

Using system calls – and when not to use them

System calls are an exciting topic in any conversation about Unix and Linux. They are one of the lowest parts when it comes to system programming in Linux. If we were to look at this from a top-down approach, the shell and the binaries we run would be at the top. Just below that, we have the standard C library functions, such as printf(), fgets(), putc(), and so on. Below them, at the lowest levels, we have the system calls, such as creat(), write(), and so on:

Figure 3.1 – High-level functions and low-level functions

When I talk about system calls here in this book, I mean system calls as C functions provided by the kernel, not the actual system call table. The system call functions we use here reside in user space, but the functions themselves execute in kernel space.

Many of the standard C library functions, such as putc(), use one or more system call functions behind the curtains. The putc() function is an excellent example; this uses write() to print a character on the screen (which is a system call). There are also standard C library functions that don't use any system calls at all, such as atoi(), which resides entirely in user space. There is no need to involve the kernel to convert a string into a number.

Generally speaking, if there is a standard C library function available, we should use that instead of a system call. System calls are often harder to work with and more primitive. Think of system calls as low-level operations, and standard C functions as high-level operations.

There are cases, though, when we need to use system calls, or when they are easier to use or more beneficial. Learning when and why to use system calls will make you a better system programmer altogether. For example, there are many filesystem operations we can perform on Linux via system calls that aren't available elsewhere. Another example when we need to use a system call is when we want to fork() a process, something we will discuss in more detail later on. In other words, we need to use system calls when we need to perform some form of system operation.

Getting ready

In this recipe, we will be using a Linux-specific system call, so you'll need a Linux computer (which you most probably already have since you're reading this book). But do notice that the sysinfo() system call won't work under FreeBSD or macOS.

How to do it…

There isn't actually much difference between using a function from the standard C library versus using a system call function. System calls in Linux are declared in unistd.h, so we need to include this file when using system calls.

  1. Write the following small program and name it sys-write.c. It uses the write() system call. Notice that we don't include stdio.h here. Since we aren't using any printf() function or any of the stdin, stdout, or stderr file streams, we don't need stdio.h here. We print directly to file descriptor 1, which is standard output. The three standard file descriptors are always opened:
    #include <unistd.h>
    int main(void)
    {
        write(1, "hello, world\n", 13);
        return 0;
    }
  2. Compile it. From now on, we will always include -Wall, -Wextra, and -pedantic to write cleaner and better code:
    $> gcc -Wall -Wextra -pedantic -std=c99 \
    > sys-write.c -o sys-write
  3. Run the program:
    $> ./sys-write 
    hello, world
  4. Now, write the same program but with the fputs() function instead—a higher-level function. Notice that we include stdio.h here, instead of unistd.h. Name the program write-chars.c:
    #include <stdio.h>
    int main(void)
    {
        fputs("hello, world\n", stdout);
        return 0;
    }
  5. Compile it:
    $> gcc -Wall -Wextra -pedantic -std=c99 \
    > write-chars.c -o write-chars
  6. Then, run it:
    $> ./write-chars 
    hello, world
  7. Now, it's time to write a program that reads some user and system information. Save the program as my-sys.c. All the system calls in the program are highlighted. This program fetches your user's ID, current working directory, the machine's total and free random-access memory (RAM), and current process ID (PID):
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/sysinfo.h>
    int main(void)
    {
       char cwd[100] = { 0 }; /* for current dir */
       struct sysinfo si; /* for system information */
       getcwd(cwd, 100); /* get current working dir */
       sysinfo(&si); /* get system information 
                      * (linux only) */
        
       printf("Your user ID is %d\n", getuid());
       printf("Your effective user ID is %d\n", 
          geteuid());
       printf("Your current working directory is %s\n", 
          cwd);
       printf("Your machine has %ld megabytes of " 
          "total RAM\n", si.totalram / 1024  / 1024);
       printf("Your machine has %ld megabytes of "
          "free RAM\n", si.freeram / 1024 / 1024);
       printf("Currently, there are %d processes "
          "running\n", si.procs);
       printf("This process ID is %d\n", getpid());
       printf("The parent process ID is %d\n", 
          getppid());
       return 0;
    }
  8. Compile the program:
    $> gcc -Wall -Wextra -pedantic -std=c99 my-sys.c -o \
    > my-sys
  9. Then, run the program. You should now see some information about your user and the machine you are using:
    $> ./my-sys 
    Your user ID is 1000
    Your effective user ID is 1000
    Your current working directory is /mnt/localnas_disk2/linux-sys/ch3/code
    Your machine has 31033 megabytes of total RAM
    Your machine has 6117 megabytes of free RAM
    Currently, there are 2496 processes running
    This process ID is 30421
    The parent process ID is 11101

How it works…

In Steps 1-6, we explored the difference between write() and fputs(). The difference might not be that obvious but write(), the system call, uses file descriptors instead of file streams. This goes for almost all system calls. File descriptors are more primitive than file streams. The same top-to-bottom approach goes for file descriptors versus file streams. File streams are layered on top of file descriptors and provide a higher-level interface. Sometimes, though, we need to use file descriptors instead, as these offer more control. File streams, on the other hand, offer a more powerful and richer input and output, with formatted output—for example, such as printf().

In Steps 7-9, we wrote a program that fetches some system and user information. Here, we included three system call-specific header files: unistd.h, sys/types.h, and sys/sysinfo.h.

We have already seen unistd.h, a common header file for system calls in Unix and Linux systems. The sys/types.h header file is another common header file for system calls, especially when it comes to getting values from the system. This header file contains special variable types; for example, uid_t and gid_t for user ID (UID) and group ID (GID). These are usually an int. Others are ino_t for inode numbers, pid_t for PIDs, and so on.

The sys/sysinfo.h header file is specifically for the sysinfo() function, which is a system call specifically for Linux, and hence this won't work under other Unix systems such as macOS, Solaris, or FreeBSD/OpenBSD/NetBSD. This header file declares the sysinfo struct, which we populate with information by calling the sysinfo() function.

The first system call we use in the program is getcwd(), to get the current working directory. The function takes two arguments: a buffer where it should save the path, and the length of that buffer.

The next system call is the Linux-specific sysinfo() function. This one gives us a lot of information. When the function executes, all data is saved to the struct sysinfo. This information includes the uptime of the system; load average; total amount of memory; available and used memory; total and available swap space; and the total number of processes running. In man 2 sysinfo, you can find information on the variables in the struct sysinfo and their data types. Further down in the code, we print some of these values using printf()—for example, si.totalram, which contains the size of the system's memory.

The rest of the system calls are called directly from printf() and returns integer values.

There's more…

There is a lot of detailed information about Linux system calls in the manual. A good starting point is man 2 intro and man 2 syscalls.

Tip

Most system calls will return -1 if an error occurs. It's generally a good idea to check for this value to detect errors.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at ₹800/month. Cancel anytime