Table of parts: https://docs.google.com/document/d/1IIOkivkS0lV9QIL16b_OjT480BxZHQfKswvvS-CVZlw/view

Syscalls

Системные вызовы

get_preview

get_preview

#include <unistd.h>

Using "man" for syscalls

Manual, справочные страницы, руководство

some_syscall(2)

man 2 some_syscall

Typical manpage sections:

See man 1 man for the list of all typical sections.

Typical manpage paragraphs (for a syscall/function):

The environment you see after calling "man" is the "less" program. Some minimal controls:

(sudo apt-get install manpages-ru)

Force Russian language for a manpage (which may be old):

LANG=ru_RU.UTF-8 man 2 dup2

Force English manpage:

LANG=en_US.UTF-8 man 2 dup2

Exercise 1

Find which flag of mount syscall should be used to make the resulting mount read-only. Which header file should be included in C in order for "mount" function to work?

Найдите какой флаг нужно указать системному вызову mount, чтобы результатирующая файловая системы была доступна только для чтения (read-only). Какой заголовочный файл нужно подключить, чтобы использовать функцию "mount" в Си?

A note about standards

There can be Linux-specific functions or flags to functions.

A term: wrapper (обёртка).

#include <stdio.h>

int main() {

        const char* hello = "Hello, world\n";

        // C way; portable everywhere

        fwrite(hello, 1, 13, stdout);

        // POSIX way; portable across POSIX

        write(1, hello, 13);

        // something in-between; with SYS_write instead of 4 should be portable across Linux.

        syscall(4, 1, hello, 13);

        // raw syscall without any wrapper; not portable (only on x86)

        asm volatile ("\

           mov $4, %%eax \n\

           mov $1, %%ebx \n\

           mov %[hhh], %%ecx \n\

           mov $13, %%edx \n\

           int $0x80 "

           : : [hhh]""(hello) : "eax","ebx","ecx","edx");

        return 0;

}

Exercise 1

Classify the following functions in three groups (listed above):

Классифицируйте следующие функции на три группы (доступно в Си, доступно в POSIX, доступно только в Linux):

open/close

int open(const char* pathname, int flags, mode_t mode);

#include <errno.h>

-1 is an error; errno is error number

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

int main() {

   int fd = open("lala.txt", O_WRONLY|O_CREAT);

   if (fd==-1) { perror("open"); } // errno, #include <errno.h>

   fprintf(stderr, "File is opened, fd=%d\n", fd);

   int ret = write(fd, "Lol\n", 4);

   fprintf(stderr, "Write is finished, %d bytes written\n", ret);

   return 0;

}

Similar program in standard C:

#include <stdio.h>

int main() {

   FILE* f = fopen("lala.txt", "w");

   if (!f) { perror("fopen"); return 1; }

   fwrite("Lol\n", 1, 4, f);

  return 0;

}

Large files

Historically files were limited by 2-4 GB. This causes "technical debt" now in some cases.

#define _FILE_OFFSET_BITS 64

(see feature_test_macros(7) for the list of such things)

Exercise 1

Add closing the file

Exercise 2

Make a program that opens file "bmm33.txt" for reading, reads 5 bytes from it into a character array, then opens file "apop4.txt" for appending and writes those 5 bytes to it, then closes all files.

Напишите программу, которая открывает файл  "bmm33.txt" на чтение, читает 5 байт из него в массив символов, затем открывает файл  "apop4.txt" на дополнение (записать в конец файла) и записывает эти 5 байт туда, затем закрывает все файлы.

read/write

ssize_t read(int fd, void *buf, size_t count);

Can return:

Safe writing of a block of memory to a fd:

const char* buf = "12345\n";

size_t remaining_bytes = 6;

int cursor = 0;

while(remaining_bytes > 0) {

   int bytes_written = write(fd, buf + cursor, remaining_bytes);

   if (bytes_written == -1) {

       if (errno == EINTR || errno == EAGAIN) continue;

       // ERROR

   }

   if (bytes_written == 0) {

      // ?

   }

   remaining_bytes -= bytes_written;

   cursor += bytes_written;

}

Using fwrite avoid necessity of handling short writes.

Exercise 1

Implement a program that exchanges two files of equal lengths in place without using any additional files, memory array or changing inode number.

Реализуйте программу, которая меняет местами содержимое двух файлов без использования временного файла, массива или смены inode файлов.

(system call "lseek")

┌─────────────────┐     ┌─────────────────────────┐     ┌────────────┐

│ File descriptor │ ──> │ Opened file with cursor │ ──> │ inode/data │

└─────────────────┘     └─────────────────────────┘     └────────────┘

fd1 = open("file", …);

fd2 = open("file", …);

fd3 = dup2(fd2, 10);

using strace

$ strace ./qq

execve("./qq", ["./qq"], [/* 26 vars */]) = 0

open("lala.txt", O_RDWR|O_LARGEFILE)        = 3

writev(2, [{"File is opened, fd=3\n", 21}, {NULL, 0}], 2File is opened, fd=3

) = 21

read(3, "L", 1)                             = 1

write(3, "\n", 1)                           = 1

read(3, "l", 1)                             = 1

write(3, "\n", 1)                           = 1

exit_group(0)                               = ?

Exercise 1

Start some your program in strace. Associate syscalls with lines in your program.

gettimeofday

#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);

struct timeval {

    time_t      tv_sec;

    suseconds_t tv_usec;

};

Exercise 1

Measure using gettimeofday(2) or clock_gettime(2) how long it takes to call getpid(2) one million times. Start your program using "time" command: "time ./myprogram". Now measure how long it takes to perform a qsort(3) on an already sorted array of 32 int elements one million times.

Измерьте при помощи gettimeofday(2) или clock_gettime(2) сколько времени занимают миллион вызовов функции getpid(2).  Запустите программу при помощи команды "time". Теперь измерьте сколько времени занимает сделать qsort(3) на уже отсортированном массиве миллион раз.

Processes

index.png

fork

pid_t fork();

pid_t wait(int *status);

Exercise 1

Write a program that does fork and waits for the child to terminate. Both parent and child should output their PIDs, see getpid(2).

Напишите программу, которая создаёт дочерний процесс и ждёт его завершения. И родитель, и дочерний процесс должны выводить свой идентификатор процесса (смотрите getpid(2)).

execve

int execve(const char* filename, char *const argv[], char *const envp[]);

There are wrappers, see execv(3).

$ readelf -a /bin/bash | grep interpreter

          [Requesting program interpreter: /lib/ld-linux.so.2]

Exercise 1

Write a shebang (#!) header to the bash script you have saved in the previous part, to make it runnable by "./myscript". You can find an examples of such header using the command below.

Допишите заголовок скрипта со ссылкой на интерпретатор (#!) в сохранённый вами в предыдущей части скрипт, чтобы его можно было запускать как программу, через ./myscript. Примеры такого заголовка можно найти, выполнив команду, приведённую ниже:

grep '#!' /usr/bin/*

Q: How many command line arguments has "grep" program in this command line?

Exercise 2

Start your "args" (or other) program manually calling dynamic linker/loader

Exercise 3

Write a program employing execve syscall. For environment variables use environ(7).

Exercise 4

Write a shell. It should read commands from stdin and start appropriate processes. Line can be split into argv using strtok(3) or using C++ means.

Напишите оболочку. Она должна читать команды из стандартного входа и запускать соответствующие процессы. Строку можно разбивать на аргументы командной строки при помощи strtok(3) или средствами C++.

Exercise 5 (optional)

Implement file redirection in your shell.

Реализуйте перенаправление в/из файлов в вашей оболочке.

Use system call dup2(2).

Exercise 6 (optional)

Implement pipelines in your shell.

Реализуйте конвейеры в вашей оболочке.

Use system call pipe(2).

Exercise 7 (optional)

Implement starting shell scripts (reading input commands from file) and executing commands from command line (-c command line argument).

Реализуйте запуск скриптов оболочки (чтения команд из файла) и выполнение команд из аргумента командной строки (-c).

./myshell # usual (interactive mode)   обычный (интерактивный) режим

./myshell file # script                   скрипт

./myshell -c "something" # command line    из командной строки

More exhaustive list of things that a process get as input

(including the program name)

Process management, /proc

cat /proc/<pid>/cmdline | tr '\0' ' '; echo

cat /proc/<pid>/environ | tr '\0' '\n'

4.png

Excercise 1

Start the "copy file loop" script (make it open the shell). Find from other tab (using ps(1) and grep(1)) PID of the bash running the script. Using /proc, tell what file descriptors has it opened, what command line parameters does it have, what environment variables and what it's current directory.

Запустите скрипт, которые в цикле пытается скопировать файл (чтобы он открыл дочернюю оболочку). Найдите из другой вкладки (используя ps(1) и grep(1)) идентификатор процесса bash, который исполняет этот скрипт. Используя /proc, cкажите какие открыты файловые дескрипторы, какие переданы параметры командной строки, какие переменные окружения и какой у него текущий каталог.


Package manager

Glossary

Installing

apt-get install another-useless-program

gdebi somefile.deb

dpkg -i somefile.deb

/var/cache/apt/archives

/etc/apt/sources.list[.d/]

Updating & searching

apt-cache search something

apt-get update

apt-file update; apt-file search

apt-get upgrade

Inspecting

dpkg -L mypackage

dpkg -I somefile.deb

dpkg -c somefile.deb

apt-cache show mypackage

apt-cache policy mypackage

Outputs of some commands

# apt-get update

...

Get:20 http://ftp.mgts.by testing/contrib i386 Packages/DiffIndex [27.8 kB]

Get:21 http://ftp.mgts.by testing/contrib Translation-en/DiffIndex [27.8 kB]

Get:22 http://ftp.mgts.by testing/main Translation-en/DiffIndex [27.9 kB]

Hit http://ftp.mgts.by jessie Release.gpg

...

Fetched 2,678 kB in 25s (106 kB/s)

Reading package lists... 33%

Reading package lists... Done

# apt-get install python-pil 

Reading package lists... Done

Building dependency tree

Reading state information... Done

The following extra packages will be installed:

 python-pil.imagetk

Suggested packages:

 python-pil-doc python-pil-dbg python-pil.imagetk-dbg

The following packages will be upgraded:

 python-pil python-pil.imagetk

2 upgraded, 0 newly installed, 0 to remove and 18 not upgraded.

Need to get 318 kB of archives.

After this operation, 0 B of additional disk space will be used.

Do you want to continue? [Y/n] y

Get:1 http://security.debian.org/ jessie/updates/main python-pil.imagetk amd64 2.6.1-2+deb8u3 [13.8 kB]

Get:2 http://security.debian.org/ jessie/updates/main python-pil amd64 2.6.1-2+deb8u3 [304 kB]

Fetched 318 kB in 0s (373 kB/s)

(Reading database ... 739003 files and directories currently installed.)

Preparing to unpack .../python-pil.imagetk_2.6.1-2+deb8u3_amd64.deb …

Unpacking python-pil.imagetk:amd64 (2.6.1-2+deb8u3) over (2.6.1-2+deb8u2) …

Preparing to unpack .../python-pil_2.6.1-2+deb8u3_amd64.deb …

Unpacking python-pil:amd64 (2.6.1-2+deb8u3) over (2.6.1-2+deb8u2) …

Setting up python-pil:amd64 (2.6.1-2+deb8u3) …

Setting up python-pil.imagetk:amd64 (2.6.1-2+deb8u3) ...

# apt-cache policy python-pil

python-pil:

 Installed: 2.6.1-2+deb8u3

 Candidate: 2.6.1-2+deb8u3

 Version table:

    3.4.2-1 0

      10 http://ftp.mgts.by/debian/ testing/main amd64 Packages

    3.4.2-1~bpo8+1 0

      100 http://http.debian.net/debian/ jessie-backports/main amd64 Packages

*** 2.6.1-2+deb8u3 0

      900 http://security.debian.org/ jessie/updates/main amd64 Packages

      100 /var/lib/dpkg/status

    2.6.1-2+deb8u2 0

      900 http://ftp.mgts.by/debian/ jessie/main amd64 Packages

# apt-cache show python-pil=2.6.1-2+deb8u3

Package: python-pil

Version: 2.6.1-2+deb8u3

Installed-Size: 1195 

Architecture: amd64

Replaces: python-imaging (<< 1.1.7+2.0.0-1.1)

Provides: python-pillow, python2.7-pil

Depends: python (<< 2.8), python (>= 2.7~), python:any (>= 2.7.5-5~), mime-support | python-pil.imagetk, libc6 (>= 2.14), libfreetype6 (>= 2.2.1), libjpeg62-turbo (>= 1.3.1), liblcms2-2 (>= 2.2+git20110628), libtiff5 (>= 4.0.3), libwebp5, libwebpdemux1, libwebpmux1, zlib1g (>= 1:1.1.4)

Suggests: python-pil-doc, python-pil-dbg

Breaks: python-imaging (<< 1.1.7+2.0.0-1.1)

Description-en: Python Imaging Library (Pillow fork)

 .....

Other info

chart

3a.png

Exercise 1:

Install some package using apt-get install

Users and permissions

Discretionary access control

Избирательное управление доступом

Права на чтение, запись, выполнение.

Opening a file

perm2.png

Reading from a file descriptor

chart

Management

Exercise 1

Create two new users on your system from command line. Add yourself to the first new user's associated group.

Создайте в системе ещё двух пользователей, из командной строки. Добавьте себя в группу первого нового пользователя.

Exercise 2

From you primary use account, create the following:

Try accessing those files from the new user account.

Из своей основной учётной записи, создайте:

Проверьте как работает контроль доступа из учётной записи другого пользователя.

More command line tools

Find(1)/Xargs(1)

find <directory> <options> <commands>

find .

find . -ls

find /home/mydir -printf "%i %P\n"

# warning: the next command tried to delete files:

find / -xdev -maxdepth 5 -iname '*.jpg' -delete

find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 file

Excersice 1

Find all regular files in current directory that are more than 3 kilobytes in size.

Найдите все обычные файлы в текущей директории, размер которых больше 3 килобайт.

Exercise 2                                                                                                                                                                                                

In all files with names matching "*.cpp" or "*.h" patterns, find lines containing word "include". Use find(1), xargs(1) and grep(1). Check handling the spaces in file names.

Во всех файлах, имена которых соответствуют маске *.cpp или *.h, найдите строки, содержащие слово "include". Проверьте как обрабатываются файлы с пробелами в имени.

Sort(1)/Uniq(1)

Excersice 1

Find out and present what sort(1) and uniq(1) does.

Исследуйте и расскажите что делают команды sort(1) и uniq(1).

substituting an executable with a shell script

#!/bin/bash

exec /use/bin/some_program "$@"

Wrong solutions:

Exercise 1

Make a shell script wrapper for your "args" program. It should log (append, >>) the current date(1) to a file before starting the program.

Напишите скрипт-обёртку для вашей программы args. Он должен протоколировать (дополнять в конец файла, >>) текущую дату ( date(1) ) в файл перед запуском программы.

environment variables

Exercise 1

What will this script output and why?

A=1

B=2

export C=3

export A

echo "A=$A B=$B C=$C D=$D"

bash -c "echo A=$A B=$B C=$C D=$D"

A=10 bash -c "echo A=$A B=$B C=$C D=$D"

{ echo "A=$A B=$B C=$C D=$D"; }

( echo "A=$A B=$B C=$C D=$D" )

{ D=4; }

echo "$D"

( D=7; )

echo "$D"

bash -c "export D=10;"

echo "$D"

Makefiles

all: myprogram

CFLAGS=-Wall

LDFLAGS=

myprogram: myprogram.o

        gcc $(CFLAGS) myprogram.o -o myprogram

myprogram.o: myprogram.c

        gcc ${LDFLAGS} -c myprogram.c -o myprogram.o

VARIABLE=value

target: dependencies

<hard tab> commands

ПЕРЕМЕННАЯ=значение

цель: зависимости

<жёсткая табуляция> команды

Variable expansion sources:

  1. Command line

$ make myprogram CFLAGS=-Werror

  1. Makefile itself or its includes

CFLAGS=-Wall

  1. Environment variables

$ CFLAGS=-Werror make myprogram

  1. Default values

Nested expansion trick

GNU make has proper "if" construct. But it can also be emulated with nested variable expansion:

PROFILE=debug

CFLAGS-debug=-Wall

CFLAGS-debug+=-ggdb

CFLAGS-debug+=-DDEBUG=1

CFLAGS-release=-O3

CFLAGS-release+=-DNDEBUG=1

CFLAGS=${CFLAGS-${PROFILE}}

Exercise 1:

Write a simple makefile for your args (or other) program.

Напишите простой сборочный файл для вашей программы "args" (или другой).

Libraries

mylib.h

struct TrickyStructure {

        int a;

        char* b;

};

void my_function(struct TrickyStructure* a);

mylib.c

#include <stdio.h>

#include "mylib.h"

static void loggg(struct TrickyStructure* a) {

    printf("Hello, str is %s, num is %d\n", a->b, a->a);

}

void my_function(struct TrickyStructure* a)

{

    loggg(a);

    a->a += 10;

}

myuser.c:

#include <stdio.h>

#include "mylib.h"

int main() {

    struct TrickyStructure ts;

    ts.a = 55;

    ts.b = "Hello, world";

    my_function(&ts);

    my_function(&ts);

    printf("The number is %d\n", ts.a);

}


command line for separate object file:

$ gcc mylib.c -c -o mylib.o

$ gcc myuser.c -c -o myuser.o

$ gcc myuser.o mylib.o -o myuser


command lines for investigation

$ objdump -t myuser.o

0000000000000000 g         F .text  000000000000004f main

0000000000000000             *UND*    0000000000000000 my_function

0000000000000000             *UND*    0000000000000000 printf

$ objdump -t mylib.o

0000000000000000 l         F .text    0000000000000031 loggg

0000000000000000             *UND*      0000000000000000 printf

0000000000000031 g         F .text    000000000000002a my_function

$ objdump -t myuser | grep 'main\|my_function\|logg'

00000000000006ff l         F .text    0000000000000031      loggg

00000000000006b0 g         F .text    000000000000004f      main

0000000000000730 g         F .text    000000000000002a      my_function

Alternative tools: nm readelf


C++ may be an issue: symbol mangling.

$ gcc -x c++ mylib.c -c -o mylib.cpp.o

$ objdump -t mylib.cpp.o | grep '\<F\>'

00...0000 l F .text        00..0031 _ZL5logggP15TrickyStructure

00...0031 g F .text        00..002a _Z11my_functionP15TrickyStructure

$ echo _Z11my_functionP15TrickyStructure | c++filt my_function(TrickyStructure*)


command line for static lib:

$ ar rcs libmylib.a mylib.o

$ objdump -t libmylib.a

$ gcc myuser.o libmylib.a -o myuser

$ gcc myuser.o -L. -lmylib -o myuser

$ gcc -L. -lmylib myuser.o -o myuser # does not work


command line for dynamic lib:

$ gcc -fPIC mylib.c -c -o mylib.o

$ gcc -shared mylib.o -o libmylib.so

$ objdump -T libmylib.so

$ rm libmylib.a

$ gcc myuser.o -L. -lmylib -o myuser

$ objdump -T myuser

$ objdump -x myuser | grep NEEDED

$ readelf -Dsd myuser   # alternative tool

$ readelf -a myuser | grep 'program interpreter'

$ ldd myuser

$ /lib64/ld-linux-x86-64.so.2 --library-path . ./myuser

$ LD_LIBRARY_PATH=. ./myuser

$ LD_DEBUG=libs LD_LIBRARY_PATH=. ./myuser


command line for dynamic lib with soname

$ gcc -shared -Wl,-soname,libmylib.so.0  mylib.o -o libmylib.so

$ ln -s libmylib.so libmylib.so.0

$ readelf -d libmylib.so | grep soname

$ gcc myuser.c -L. -lmylib -o myuser

$ LD_LIBRARY_PATH=. ./myuser


command line for building with rpath

$ gcc myuser.c -L. -lmylib -Wl,-rpath -Wl,'$ORIGIN' -o myuser

$ ./myuser


Exercise 1:

Write a simple C library program and C library user.

Напишите простую библиотеку на C и программы, использующую эту библиотеку.

Exercise 2:

Use LD_DEBUG=libs environment variable to see the loading works. Use readelf -s and objdump -t both on the library and on your program to see the symbols. Use ldd(1).

Используйте переменную окружения LD_DEBUG со значением "libs" чтобы увидеть, как работает загрузка библиотеки. Используйте readelf -s и objdump -t на библиотеку и программу, чтобы увидеть символы. Используйте ldd(1).

Exercise 3:

Write a makefile that builds both library and user.

Напишите Makefile, который собирает и библиотеку, и программу.

Exercise 4:

Write a library user that uses dynamic loading (dlopen(3)).

Напишите программы, динамически загружающие вашу библиотеку через dlopen(3).

Exercise 5:

implementing LD_PRELOAD

symver

API Application Programming Interface - header file

ABI Application Binary Interface - symbols and their interpretation

Compatible ABI - can replace library file and existing program works

Compatible API - can replace library file and header, recompile program, then it works.

Version: 3.6.5

Patchlevel: 3.6.6

  1. No change in API/ABI
  2. Bugfix

Minor version: 3.7.0

  1. Compatible change in API/ABI
  1. New function added
  2. More flags added
  1. More features

Major version: 4.0.0

  1. Incompatible change in API/ABI
  1. Something removed
  2. Something reordered
  3. Something renamed
  1. Redesign of API

Depends: libmycode (>= 3.6, << 4)

API change example:

Baseline: version 1.2.3

struct Qqq {

    int a;

    char* b;

};

void zzz(struct Qqq *a);

Minor upgrade: version 1.2.4

struct Qqq {

    int a;

    char* b;

};

void zzz(struct Qqq *a);

Mid upgrade: version 1.3.0

struct Qqq {

    int a;

    char* b;

};

void zzz(struct Qqq *a);

void yyy(long* qwerty);

Major upgrade: version 2.0.0

struct Qqq {

    char* b;

    int a;

    float u;

};

int zzz(struct Qqq *a, int lol);

Library files in Linux

Sources of program configuration

In order of priority:

  1. Command line argument
  2. File content
  3. Filename extension
  4. Environment variable
  5. User's settings file
  6. System settings file
  7. Default value

$ cat ~/.config/myprogram

file_type=type5

$ cat /etc/myprogram.conf

file_type=type6

$ hd myfile.type3

00000000  46 54 59 50 32 03 15 00  00 00 00 00 00 00 00 00  |FTYP2...........|

00000010  00 00 00 4d 4b 61 40 40  71 77 65 00 00 00 00 00  |...MKa@@qwe.....|

00000020  00 00 00 00 00 00 00 00  00                       |.........|

00000029

$ FILE_TYPE=type4 some_program --file-type=type1 myfile.type3