OS: Exercise 2: System calls and manual pages

You can do this exercise in any size group you wish. Don't worry if you find some of the questions difficult to answer, since they may be intended to initiate a discussion rather than have a single correct answer.

There might also be more questions than you have time to answer, but do as many as you can.

Preparations

Before this exercise, you should (ideally) have: The manual pages for the Linux system calls can be read using the command man in a terminal window, or in several places on the web, for exampel on the site linux.die.net.

Sections of the the manual pages:

Sometimes there are manual pages with the same name in two or more than sections. To get the manual page from a certain section, add the section number to the command. For example, man write will show you the manual page for the user command write, which lets you send a message to another user who is logged in on the same machine. To get the manual page for the system call write, give the command man 2 write.

Uppgift 1

Explain the difference between the two special files /dev/random and /dev/urandom.

Uppgift 2

a) Vad gör systemanropet wait?

b) Vilket eller vilka problem är det man vill lösa med det systemanropet?

Uppgift 3

Systemanropet open öppnar en fil. Det tar tre argument: ett filnamn, ett heltal med flaggor (som anges med bitarna i talet) och en (som bara behövs ibland) filskyddskod.

a) De vanligaste flaggorna är O_RDONLY, O_WRONLY, O_RDWR, O_CREAT och O_TRUNC. Vad innebär de?

b) När behövs det tredje argumentet, med filskyddskoden?

Uppgift 4

Här är ett C-program för Linux, kallat "program 1", som läser en gigabyte data från en fil:

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

#define KILO 1024
#define MEGA (KILO*KILO) // 1048576
#define GIGA (KILO*KILO*KILO) // 1073741824

char data[GIGA];

int main(void) {
    int fd = open("data.bin", O_RDONLY);
    for (int i = 0; i < KILO; ++i) {
        read(fd, &data[i * MEGA], MEGA);
    }
    close(fd);
}

I en annan version av samma program, kallat "program 2", byter vi ut for-loopen mot den här:

    for (int i = 0; i < MEGA; ++i) {
        read(fd, &data[i * KILO], KILO);
    }

(Skillnaden är alltså att vi bytt plats på "MEGA" och "KILO".)

Programmen gör samma sak, och de fungerar korrekt, men man kan förvänta sig att ett av programmen går betydligt snabbare än det andra.

a) Vilket program är snabbast?

b) Vad beror skillnaden på?

c) Nu gör vi ännu en version av programmet, kallat "program 3", där for-loopen ser ut så här:

    for (int i = 0; i < GIGA; ++i) {
        read(fd, &data[i], 1);
    }

Om program 2 tog en sekund att köra, hur lång tid kan man gissa att program 3 tar?

Uppgift 5

Här är ett C-program för Linux. Vad skrivs ut när programmet körs?

#include <stdio.h>
#include <unistd.h>

int x = 1;

int main(void) {
    int y = 1;
    fork();
    x = x + 1;
    y = y + 1;
    printf("Hej! x = %d, y = %d!\n", x, y);
    fork();
    x = x + 1;
    y = y + 1;
    printf("Hopp! x = %d, y = %d!\n", x, y);
}

Uppgift 6

För att starta ett program i Unix/Linux gör man först fork, som "delar processen i två" genom att skapa en (nästan) exakt kopia av processen, och därefter låter man den nyligen skapade processen starta det nya programmet med systemanropet execve eller någon av de olika biblioteksfunktionerna i exec-familjen.

Är det inte väldigt ineffektivt att först skapar fork en kopia av hela processen, med minnesinnehåll och det körande programmets körbara kod och allt, och direkt därefter kastar execve bort allt innehållet i processen och ersätter det med ett nystartat program?


Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se), May 2, 2022