Chapter 18: C and Operating Systems

C and Operating Systems

C and Operating Systems
C programming

Welcome to Chapter 18 of our C programming series – C and operating Systems! Today, we take a step towards an intriguing aspect of C: its interaction with operating systems. Yes, C isn’t just about crunching numbers and manipulating strings. It’s a vital tool for engaging with the very heart of your computer – the operating system.

Our exploration will cover two fascinating subjects – System Calls and Shell Scripts. By delving into these, you’ll learn to wield C’s power more effectively, amplifying your coding prowess.

System Calls

Firstly, let’s demystify ‘system calls’. System calls are the primary method by which a user-space program interacts with the operating system’s kernel. They provide an interface to the services made available by an operating system, such as process management, file operations, device management, and more.

In C, we have an array of functions to facilitate these system calls. For instance, if we want to interact with files, we have system calls like open(), read(), write(), and close(). To manage processes, we can use fork(), wait(), exit(), among others.

Here’s a simple example of a system call in action, creating a file and writing to it:

#include <fcntl.h>
#include <unistd.h>

int main() {
    int file_desc = open("test.txt", O_CREAT | O_WRONLY, 0644);
    if (file_desc < 0) {
        // Handle error
    }

    char* message = "Hello, System Calls!";
    write(file_desc, message, strlen(message));
    close(file_desc);

    return 0;
}

This program opens a file named “test.txt” (creating it if it doesn’t exist), writes a message into it, and finally closes the file. In these simple lines of code, we’ve invoked three system calls!

Writing Shell Scripts in C

Next, let’s traverse to the realm of shell scripting in C. Yes, you read that right! Although you might commonly associate shell scripts with Bash or Python, C isn’t a stranger to this territory. Given its low-level prowess, C can provide performance advantages in resource-intensive scripts.

Writing a shell script in C involves using system calls to execute other programs or command-line utilities. The system() function serves this purpose. It allows us to run system commands within our C program, almost as if we’re typing them into a terminal.

Let’s examine a simple C program that uses system() to list the contents of the current directory:

#include <stdlib.h>

int main() {
    int return_value;
    return_value = system("ls -l");

    return return_value;
}

In this example, the system() function runs the ls -l command, which lists all files in the directory in long format. Remember, the argument to system() is exactly what you would type into the terminal.

Real Word Example

Let’s take a real-world example of an application that logs system activities by recording the CPU utilization and memory usage over time. This type of program might be used by system administrators to monitor the health and performance of a server.

In this example, we’ll use the system() function to call command-line tools that retrieve CPU and memory information.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAX_BUFFER_SIZE 128

int main() {
    FILE *logFile;
    char buffer[MAX_BUFFER_SIZE];
    time_t current_time;

    // Open the log file
    logFile = fopen("system_log.txt", "a");
    if (logFile == NULL) {
        printf("Error opening file.\n");
        exit(1);
    }

    while(1) {
        // Get the current time
        time(&current_time);
        char *time_str = ctime(&current_time);

        // Remove the newline character from the time string
        time_str[strlen(time_str) - 1] = '\0';

        // Log the CPU usage
        FILE *cpuFile = popen("top -bn1 | grep 'Cpu(s)'", "r");
        fgets(buffer, MAX_BUFFER_SIZE, cpuFile);
        fprintf(logFile, "%s CPU usage: %s", time_str, buffer);
        pclose(cpuFile);

        // Log the memory usage
        FILE *memFile = popen("free -h | grep 'Mem:'", "r");
        fgets(buffer, MAX_BUFFER_SIZE, memFile);
        fprintf(logFile, "%s Memory usage: %s", time_str, buffer);
        pclose(memFile);

        // Sleep for 5 seconds
        sleep(5);
    }

    fclose(logFile);

    return 0;
}

This program continuously logs CPU and memory usage every 5 seconds. It uses popen(), a function which creates a pipe between the program and the output of a shell command, allowing the program to capture this output. In this case, we’re using it to capture the output of top and free, which provide information on CPU and memory usage, respectively.

Remember that in a production environment, you would need to add error checking and possibly use lower-level system calls to retrieve system information, since calling command-line tools using system() or popen() can be a security risk. But for the purpose of this example, we’re focusing on the use of system calls to interact with the operating system.

Conclusion

As we conclude Chapter 18, let’s take a moment to acknowledge C‘s profound versatility. From direct interaction with the operating system through system calls, to mimicking terminal operations with shell scripting, C stands as a testament to power and flexibility in programming.

Our journey today may seem a bit daunting, especially if you’re new to the idea of mingling with operating systems. However, remember the golden rule – practice makes perfect! So, don’t shy away from experimenting and testing out these concepts.

And with that, we’re signing off from Chapter 18. Get ready to dive deeper into the vast sea of C programming in our upcoming chapters! Happy coding!

Chapter 18: C and Operating Systems
Scroll to top
error: Content is protected !!