Sisteme de Operare Partea 2
Curs Avansat de Sisteme de Operare — Partea II¶
Module Kernel, Drivere de Dispozitiv, Networking, Debugging și Securitate¶
Cuprins — Partea II¶
- Module kernel încărcabile (LKM)
- Modelul de dispozitive Linux (Device Model)
- Drivere de tip Character Device
- Drivere de tip Block Device
- Drivere Platform și Device Tree
- Subsistemul I²C — driver client complet
- Subsistemul SPI
- Subsistemul GPIO și pinctrl
- Stiva de rețea Linux (Networking Stack)
- Debugging și tracing în kernel
- Securitatea kernel-ului
- Virtualizare și containere
11. Module kernel încărcabile (LKM)¶
11.1 Ce este un modul kernel?¶
Un modul kernel (LKM — Loadable Kernel Module) este o bucată de cod compilat care poate fi încărcată și descărcată din kernel la runtime, fără a reporni sistemul. Modulele sunt mecanismul principal de extensie: drivere de dispozitiv, sisteme de fișiere, protocoale de rețea, filtre de pachete.
# Comenzi de bază:
lsmod # Listează modulele încărcate
modinfo e1000e # Informații despre un modul
sudo insmod ./mymod.ko # Încarcă modul (fără dependențe)
sudo rmmod mymod # Descarcă modul
sudo modprobe e1000e # Încarcă modul + dependențe
sudo modprobe -r e1000e # Descarcă modul + dependențe
# Modulele sunt stocate în:
ls /lib/modules/$(uname -r)/kernel/drivers/
11.2 Primul modul kernel — Hello World¶
/* hello.c — cel mai simplu modul kernel */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Student");
MODULE_DESCRIPTION("Hello World Kernel Module");
MODULE_VERSION("1.0");
/* Funcția de inițializare — apelată la insmod/modprobe */
static int __init hello_init(void)
{
pr_info("Hello, Kernel World! PID=%d (%s)\n",
current->pid, current->comm);
return 0; /* 0 = succes, negativ = eroare (modul nu se încarcă) */
}
/* Funcția de cleanup — apelată la rmmod */
static void __exit hello_exit(void)
{
pr_info("Goodbye, Kernel World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
11.3 Sistemul de build — Kbuild Makefile¶
# Makefile pentru modul extern (out-of-tree)
obj-m += hello.o
# Pentru module cu mai multe fișiere sursă:
# obj-m += mydriver.o
# mydriver-objs := main.o utils.o hw.o
# Directorul sursei kernel-ului
KDIR ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
# Cross-compilare pentru ARM:
# make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
# KDIR=/path/to/arm64/kernel/build
# Build și test:
make
sudo insmod hello.ko
dmesg | tail -1 # "Hello, Kernel World! PID=1234 (insmod)"
sudo rmmod hello
dmesg | tail -1 # "Goodbye, Kernel World!"
11.4 Parametri de modul¶
#include <linux/moduleparam.h>
static int count = 1;
static char *name = "world";
static int values[4];
static int nr_values;
static bool debug = false;
/* Declarare parametri: */
module_param(count, int, 0644); /* /sys/module/hello/parameters/count */
MODULE_PARM_DESC(count, "Number of greetings");
module_param(name, charp, 0444); /* read-only */
MODULE_PARM_DESC(name, "Name to greet");
module_param_array(values, int, &nr_values, 0644);
MODULE_PARM_DESC(values, "Array of values");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Enable debug output");
/* Permisiuni:
* 0 = nu apare în sysfs
* 0444 = read-only
* 0644 = read-write (root poate modifica la runtime)
*/
static int __init hello_init(void)
{
int i;
for (i = 0; i < count; i++)
pr_info("Hello, %s!\n", name);
if (debug)
pr_debug("Debug: nr_values=%d\n", nr_values);
return 0;
}
# Încărcare cu parametri:
sudo insmod hello.ko count=3 name="Linux" debug=1
sudo insmod hello.ko values=10,20,30
# Modificare parametri la runtime (dacă permisiunile permit):
echo 5 > /sys/module/hello/parameters/count
11.5 Exportul simbolurilor¶
/* Un modul poate exporta funcții/variabile pentru alte module: */
/* modul_A.c — exportă funcția */
int my_shared_function(int x)
{
return x * 2;
}
EXPORT_SYMBOL(my_shared_function); /* Disponibil tuturor modulelor */
EXPORT_SYMBOL_GPL(my_shared_function); /* Doar modulelor GPL */
/* modul_B.c — folosește funcția */
extern int my_shared_function(int x);
static int __init b_init(void)
{
pr_info("Result: %d\n", my_shared_function(21));
return 0;
}
/* Dependența este înregistrată automat:
* modinfo modul_B.ko | grep depends
* depends: modul_A
*/
11.6 Anatomia unui fișier .ko¶
# Un .ko este un ELF cu secțiuni speciale:
file hello.ko
# hello.ko: ELF 64-bit LSB relocatable, x86-64, ...
# Secțiuni importante:
readelf -S hello.ko
# .text — codul compilat
# .init.text — funcția __init (eliberată după init)
# .exit.text — funcția __exit
# .rodata — constante (inclusiv stringurile MODULE_*)
# .modinfo — metadate modul (license, author, description, params)
# .gnu.linkonce.this_module — structura struct module
# __versions — CRC-uri ale simbolurilor kernel (versioning)
# Verificare versiune kernel compatibilă:
modinfo hello.ko
# vermagic: 6.1.0-amd64 SMP preempt mod_unload
12. Modelul de dispozitive Linux (Device Model)¶
12.1 Concepte fundamentale¶
Modelul de dispozitive Linux (introdus în kernel 2.6) organizează hardware-ul și driverele într-o ierarhie unificată, vizibilă prin /sys.
Trei abstracții cheie:
┌──────────────────────────────────────────────────────────┐
│ DEVICE MODEL │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │
│ │ Bus │ │ Device │ │ Driver │ │
│ │ │ │ │ │ │ │
│ │ Definește │ │ Reprezintă │ │ Știe cum să │ │
│ │ protocolul │ │ un hardware │ │ controleze │ │
│ │ de comuni- │ │ concret │ │ un tip de │ │
│ │ care │ │ conectat │ │ dispozitiv │ │
│ │ │ │ la un bus │ │ │ │
│ │ PCI, USB, │ │ pci_dev, │ │ pci_driver, │ │
│ │ I2C, SPI, │ │ usb_dev, │ │ usb_driver, │ │
│ │ platform │ │ i2c_client │ │ i2c_driver │ │
│ └──────┬──────┘ └──────┬──────┘ └───────┬────────┘ │
│ │ │ │ │
│ └────────┬────────┘ │ │
│ │ match() │ │
│ └────────────────────────────┘ │
│ probe() │
│ │
│ Când un device nou apare pe un bus, kernel-ul verifică │
│ dacă există un driver care se potrivește (match). │
│ Dacă da, apelează probe() al driverului. │
│ La deconectare: apelează remove(). │
└──────────────────────────────────────────────────────────┘
12.2 Bus → Device → Driver: fluxul de potrivire¶
1. BUS detectează un dispozitiv nou
(ex: PCIe enumeration, USB hotplug, Device Tree parsing)
2. Kernel creează struct device și îl înregistrează pe bus
3. Bus-ul iterează prin toți driverele înregistrate:
bus->match(device, driver) → verifică compatibilitate
Criterii de match (depind de bus):
- PCI: vendor_id + device_id
- USB: vendor_id + product_id + class
- I2C: adresă + compatible string (DT)
- SPI: compatible string (DT)
- Platform: compatible string (DT) sau name
4. Dacă match → bus apelează driver->probe(device)
probe() inițializează hardware-ul, alocă resurse, înregistrează interfețe
5. La deconectare → driver->remove(device)
remove() eliberează resurse, dezînregistrează interfețe
12.3 Ierarhia /sys (sysfs)¶
/sys/
├── bus/ # Toate bus-urile din sistem
│ ├── pci/
│ │ ├── devices/ # Symlinks către dispozitive PCI
│ │ │ ├── 0000:00:02.0 → ../../../devices/pci0000:00/0000:00:02.0
│ │ │ └── ...
│ │ └── drivers/ # Drivere PCI înregistrate
│ │ ├── i915/
│ │ ├── nvme/
│ │ └── ...
│ ├── i2c/
│ ├── spi/
│ ├── usb/
│ └── platform/
│
├── class/ # Dispozitive grupate pe funcție
│ ├── net/ # eth0, wlan0, ...
│ ├── block/ # sda, nvme0n1, ...
│ ├── input/ # mouse, kbd, ...
│ ├── tty/ # ttyS0, ttyUSB0, ...
│ ├── gpio/
│ ├── hwmon/ # Senzori temperatură, fan
│ └── leds/
│
├── devices/ # Arborele fizic complet
│ ├── system/
│ │ ├── cpu/cpu0/ # CPU-uri
│ │ └── memory/ # Blocuri DIMM
│ ├── pci0000:00/ # Root PCI bus
│ │ ├── 0000:00:02.0/ # GPU integrat
│ │ └── 0000:00:1f.0/ # Chipset (LPC/eSPI)
│ └── platform/ # Dispozitive platform
│ ├── serial8250/
│ └── ...
│
├── module/ # Module kernel încărcate
│ ├── hello/
│ │ └── parameters/
│ │ └── count
│ └── e1000e/
│
└── firmware/ # Informații firmware (ACPI, DMI, DT)
├── acpi/
└── devicetree/
12.4 Structuri fundamentale¶
/* struct device — baza tuturor dispozitivelor */
struct device {
struct kobject kobj; /* Obiect sysfs */
struct device *parent; /* Dispozitivul părinte */
struct bus_type *bus; /* Bus-ul de care aparține */
struct device_driver *driver; /* Driverul asociat */
void *platform_data; /* Date specifice platformei */
void *driver_data; /* Date private ale driverului */
struct device_node *of_node; /* Nodul Device Tree */
dev_t devt; /* Major:Minor number */
struct class *class; /* Clasa (net, block, input...) */
const struct attribute_group **groups; /* Atribute sysfs */
void (*release)(struct device *dev); /* Destructor */
/* ... */
};
/* Macro esențial — stochează date private per dispozitiv: */
void dev_set_drvdata(struct device *dev, void *data);
void *dev_get_drvdata(struct device *dev);
/* Macro-uri de logging preferate (include automat numele dispozitivului): */
dev_info(&pdev->dev, "Device initialized, IRQ=%d\n", irq);
dev_err(&pdev->dev, "Failed to allocate buffer\n");
dev_warn(&pdev->dev, "Timeout waiting for response\n");
dev_dbg(&pdev->dev, "Register value: 0x%08x\n", val);
12.5 struct bus_type¶
struct bus_type {
const char *name; /* "pci", "usb", "i2c", "spi", "platform" */
int (*match)(struct device *dev, struct device_driver *drv);
int (*probe)(struct device *dev); /* Apelat după match reușit */
void (*remove)(struct device *dev);
int (*uevent)(const struct device *dev, struct kobj_uevent_env *env);
/* ... */
};
/* Exemplu: bus-ul I²C face match pe of_device_id (DT compatible)
* sau i2c_device_id (tabela tradițională). */
13. Drivere de tip Character Device¶
13.1 Ce este un character device?¶
Un character device (char dev) oferă acces secvențial la un flux de bytes (stream). Aplicațiile interacționează cu el prin operații standard pe fișiere: open, read, write, ioctl, close. Exemple: porturi seriale (/dev/ttyS0), RNG (/dev/urandom), framebuffer (/dev/fb0), custom devices.
13.2 Major și minor numbers¶
Fiecare char device este identificat prin doi numeri:
- Major: identifică driverul (tipul de dispozitiv)
- Minor: identifică instanța specifică gestionată de acel driver
ls -la /dev/ttyS*
# crw-rw---- 1 root dialout 4, 64 ... /dev/ttyS0 (major=4, minor=64)
# crw-rw---- 1 root dialout 4, 65 ... /dev/ttyS1 (major=4, minor=65)
ls -la /dev/null /dev/zero /dev/random
# crw-rw-rw- 1 root root 1, 3 ... /dev/null (major=1, minor=3)
# crw-rw-rw- 1 root root 1, 5 ... /dev/zero (major=1, minor=5)
# crw-rw-rw- 1 root root 1, 8 ... /dev/random (major=1, minor=8)
13.3 Driver char device complet¶
/* mychardev.c — character device driver complet */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example Character Device Driver");
#define DEVICE_NAME "mychardev"
#define BUFFER_SIZE 4096
/* === Date globale ale driverului === */
static dev_t dev_num; /* Major:Minor alocat */
static struct cdev my_cdev; /* Structura character device */
static struct class *my_class; /* Clasa pentru /dev automat */
static char device_buffer[BUFFER_SIZE];
static size_t buffer_len;
static DEFINE_MUTEX(buffer_mutex);
/* === Operația open === */
static int mydev_open(struct inode *inode, struct file *file)
{
pr_info("mychardev: opened by %s (pid %d)\n",
current->comm, current->pid);
return 0;
}
/* === Operația release (close) === */
static int mydev_release(struct inode *inode, struct file *file)
{
pr_info("mychardev: closed\n");
return 0;
}
/* === Operația read === */
static ssize_t mydev_read(struct file *file, char __user *buf,
size_t count, loff_t *offset)
{
size_t to_read;
mutex_lock(&buffer_mutex);
/* Verifică dacă am ajuns la sfârșitul datelor */
if (*offset >= buffer_len) {
mutex_unlock(&buffer_mutex);
return 0; /* EOF */
}
/* Calculează câți bytes putem furniza */
to_read = min(count, buffer_len - (size_t)*offset);
/* Copiază date din kernel buffer în userspace */
if (copy_to_user(buf, device_buffer + *offset, to_read)) {
mutex_unlock(&buffer_mutex);
return -EFAULT;
}
*offset += to_read;
mutex_unlock(&buffer_mutex);
pr_info("mychardev: read %zu bytes\n", to_read);
return to_read;
}
/* === Operația write === */
static ssize_t mydev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
size_t to_write;
mutex_lock(&buffer_mutex);
to_write = min(count, (size_t)(BUFFER_SIZE - 1));
if (copy_from_user(device_buffer, buf, to_write)) {
mutex_unlock(&buffer_mutex);
return -EFAULT;
}
buffer_len = to_write;
device_buffer[buffer_len] = '\0';
mutex_unlock(&buffer_mutex);
pr_info("mychardev: wrote %zu bytes\n", to_write);
return to_write;
}
/* === Operația ioctl === */
#define MYDEV_IOC_MAGIC 'M'
#define MYDEV_IOC_RESET _IO(MYDEV_IOC_MAGIC, 0)
#define MYDEV_IOC_GETSIZE _IOR(MYDEV_IOC_MAGIC, 1, int)
static long mydev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int size;
switch (cmd) {
case MYDEV_IOC_RESET:
mutex_lock(&buffer_mutex);
memset(device_buffer, 0, BUFFER_SIZE);
buffer_len = 0;
mutex_unlock(&buffer_mutex);
pr_info("mychardev: buffer reset\n");
return 0;
case MYDEV_IOC_GETSIZE:
mutex_lock(&buffer_mutex);
size = buffer_len;
mutex_unlock(&buffer_mutex);
if (copy_to_user((int __user *)arg, &size, sizeof(size)))
return -EFAULT;
return 0;
default:
return -ENOTTY; /* Comandă necunoscută */
}
}
/* === Tabela de operații pe fișier === */
static const struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.open = mydev_open,
.release = mydev_release,
.read = mydev_read,
.write = mydev_write,
.unlocked_ioctl = mydev_ioctl,
};
/* === Inițializare modul === */
static int __init mydev_init(void)
{
int ret;
/* 1. Alocă dynamic major+minor number */
ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
if (ret < 0) {
pr_err("Failed to allocate chrdev region\n");
return ret;
}
pr_info("mychardev: registered major=%d, minor=%d\n",
MAJOR(dev_num), MINOR(dev_num));
/* 2. Inițializează structura cdev și asociază fops */
cdev_init(&my_cdev, &mydev_fops);
my_cdev.owner = THIS_MODULE;
/* 3. Adaugă cdev la kernel */
ret = cdev_add(&my_cdev, dev_num, 1);
if (ret < 0) {
unregister_chrdev_region(dev_num, 1);
return ret;
}
/* 4. Creează clasa de dispozitiv (apare în /sys/class/) */
my_class = class_create(DEVICE_NAME);
if (IS_ERR(my_class)) {
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
return PTR_ERR(my_class);
}
/* 5. Creează intrarea în /dev (prin udev/devtmpfs) */
if (IS_ERR(device_create(my_class, NULL, dev_num, NULL, DEVICE_NAME))) {
class_destroy(my_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
return -ENOMEM;
}
pr_info("mychardev: /dev/%s created\n", DEVICE_NAME);
return 0;
}
/* === Cleanup modul === */
static void __exit mydev_exit(void)
{
device_destroy(my_class, dev_num);
class_destroy(my_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
pr_info("mychardev: removed\n");
}
module_init(mydev_init);
module_exit(mydev_exit);
13.4 Testarea din userspace¶
# Încarcă modulul:
sudo insmod mychardev.ko
# Verifică:
ls -la /dev/mychardev
cat /proc/devices | grep mychardev
# Scrie date:
echo "Hello from userspace!" > /dev/mychardev
# Citește date:
cat /dev/mychardev
# Output: Hello from userspace!
# Descarcă:
sudo rmmod mychardev
/* Testare ioctl din C: */
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define MYDEV_IOC_MAGIC 'M'
#define MYDEV_IOC_RESET _IO(MYDEV_IOC_MAGIC, 0)
#define MYDEV_IOC_GETSIZE _IOR(MYDEV_IOC_MAGIC, 1, int)
int main(void)
{
int fd = open("/dev/mychardev", O_RDWR);
if (fd < 0) { perror("open"); return 1; }
/* Scrie ceva */
write(fd, "Test data", 9);
/* Citește dimensiunea prin ioctl */
int size;
ioctl(fd, MYDEV_IOC_GETSIZE, &size);
printf("Buffer size: %d\n", size);
/* Reset buffer */
ioctl(fd, MYDEV_IOC_RESET);
close(fd);
return 0;
}
13.5 Miscdevice — shortcut pentru device-uri simple¶
/* misc device = char device simplificat (major 10, automatic) */
#include <linux/miscdevice.h>
static const struct file_operations my_misc_fops = {
.owner = THIS_MODULE,
.read = mydev_read,
.write = mydev_write,
};
static struct miscdevice my_misc = {
.minor = MISC_DYNAMIC_MINOR, /* Alocare automată minor */
.name = "mymiscdev", /* /dev/mymiscdev */
.fops = &my_misc_fops,
};
static int __init my_init(void)
{
return misc_register(&my_misc); /* O singură linie! */
}
static void __exit my_exit(void)
{
misc_deregister(&my_misc);
}
module_init(my_init);
module_exit(my_exit);
14. Drivere de tip Block Device¶
14.1 Diferența block vs. character¶
| Proprietate | Character Device | Block Device |
|---|---|---|
| Acces | Secvențial (stream) | Random access (sectoare) |
| Unitate | Byte | Bloc (512B, 4KB) |
| Buffering | Nu (de obicei) | Da (page cache, I/O scheduler) |
| Exemple | Serial, RNG, input | HDD, SSD, RAM disk, loop |
| Syscalls | read/write/ioctl | read/write + mount filesystem |
14.2 Arhitectura I/O pentru block devices¶
Aplicație: read(fd, buf, 4096)
│
▼
┌────────────────────┐
│ VFS (ext4, xfs...) │ Convertește offset fișier → blocuri logice
└────────┬───────────┘
│
┌────────▼───────────┐
│ Page Cache │ Verifică dacă blocul e deja în RAM
└────────┬───────────┘ Cache hit → returnează imediat
│ Cache miss
┌────────▼───────────┐
│ Block Layer │ Creează struct bio (Block I/O)
│ (bio, request) │ Merge/sortare cereri, I/O scheduling
└────────┬───────────┘
│
┌────────▼───────────┐
│ Block Driver │ Convertește request → comenzi hardware
│ (NVMe, SCSI, │ Trimite la controller
│ virtio-blk) │
└────────┬───────────┘
│
┌───▼────┐
│ Hardware│
└────────┘
14.3 Schedulere I/O¶
# Vizualizare scheduler curent:
cat /sys/block/sda/queue/scheduler
# [mq-deadline] kyber bfq none
# Schimbare:
echo bfq > /sys/block/sda/queue/scheduler
| Scheduler | Descriere | Potrivit pentru |
|---|---|---|
| none | Fără reordonare (FIFO direct) | NVMe (latență mică) |
| mq-deadline | Sortare + deadline per request | HDD, SSD general |
| bfq | Budget Fair Queuing (fair per proces) | Desktop interactiv |
| kyber | Lightweight, latency-focused | Servere NVMe |
14.4 RAM Disk — block driver simplificat¶
/* ramdisk.c — block device backed by RAM (simplificat) */
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/blk-mq.h>
#define SECTOR_SIZE 512
#define NR_SECTORS 2048 /* 1 MB */
#define DEVICE_NAME "myramdisk"
static struct gendisk *my_disk;
static struct blk_mq_tag_set tag_set;
static u8 *disk_data; /* Bufferul RAM */
/* Procesarea unui request I/O: */
static blk_status_t my_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
struct request *rq = bd->rq;
struct bio_vec bvec;
struct req_iterator iter;
sector_t sector = blk_rq_pos(rq);
size_t offset;
blk_mq_start_request(rq);
rq_for_each_segment(bvec, rq, iter) {
void *buf = page_address(bvec.bv_page) + bvec.bv_offset;
unsigned int len = bvec.bv_len;
offset = sector * SECTOR_SIZE;
if (rq_data_dir(rq) == READ) {
memcpy(buf, disk_data + offset, len);
} else {
memcpy(disk_data + offset, buf, len);
}
sector += len / SECTOR_SIZE;
}
blk_mq_end_request(rq, BLK_STS_OK);
return BLK_STS_OK;
}
static const struct blk_mq_ops my_mq_ops = {
.queue_rq = my_queue_rq,
};
static const struct block_device_operations my_bdev_ops = {
.owner = THIS_MODULE,
};
static int __init ramdisk_init(void)
{
int ret;
disk_data = vzalloc(NR_SECTORS * SECTOR_SIZE);
if (!disk_data)
return -ENOMEM;
memset(&tag_set, 0, sizeof(tag_set));
tag_set.ops = &my_mq_ops;
tag_set.nr_hw_queues = 1;
tag_set.queue_depth = 128;
tag_set.numa_node = NUMA_NO_NODE;
tag_set.cmd_size = 0;
tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
ret = blk_mq_alloc_tag_set(&tag_set);
if (ret)
goto err_free_data;
my_disk = blk_mq_alloc_disk(&tag_set, NULL, NULL);
if (IS_ERR(my_disk)) {
ret = PTR_ERR(my_disk);
goto err_free_tagset;
}
strcpy(my_disk->disk_name, DEVICE_NAME);
my_disk->major = 0; /* Alocare dinamică */
my_disk->first_minor = 0;
my_disk->minors = 1;
my_disk->fops = &my_bdev_ops;
set_capacity(my_disk, NR_SECTORS);
ret = add_disk(my_disk);
if (ret)
goto err_cleanup_disk;
pr_info("myramdisk: registered (%d sectors, %d KB)\n",
NR_SECTORS, NR_SECTORS * SECTOR_SIZE / 1024);
return 0;
err_cleanup_disk:
put_disk(my_disk);
err_free_tagset:
blk_mq_free_tag_set(&tag_set);
err_free_data:
vfree(disk_data);
return ret;
}
static void __exit ramdisk_exit(void)
{
del_gendisk(my_disk);
put_disk(my_disk);
blk_mq_free_tag_set(&tag_set);
vfree(disk_data);
pr_info("myramdisk: removed\n");
}
module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_LICENSE("GPL");
# Testare:
sudo insmod ramdisk.ko
ls /dev/myramdisk
# Creare filesystem și mount:
sudo mkfs.ext4 /dev/myramdisk
sudo mount /dev/myramdisk /mnt
echo "Hello RAM disk!" > /mnt/test.txt
cat /mnt/test.txt
sudo umount /mnt
sudo rmmod ramdisk
15. Drivere Platform și Device Tree¶
15.1 Ce este un platform device?¶
Platform devices sunt dispozitive care nu se află pe un bus auto-enumerabil (PCI, USB). Sunt descrise static prin Device Tree (ARM, RISC-V) sau ACPI (x86). Cele mai comune în sisteme embedded: GPIO controllers, UART, SPI, I²C, timere, controlere DMA, controlere de întreruperi.
15.2 Descrierea hardware-ului în Device Tree¶
/* Fragment DTS pentru un LED controller și un senzor de temperatură */
/ {
/* Nodul dispozitivului nostru */
my-led-controller@40010000 {
compatible = "mycompany,led-ctrl-v2"; /* Cheia de match! */
reg = <0x40010000 0x100>; /* Adresa MMIO + dimensiune */
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>; /* IRQ */
clocks = <&rcc TIM2_CLK>; /* Referință la ceas */
clock-names = "timer";
#address-cells = <1>;
#size-cells = <0>;
status = "okay"; /* Activat */
led@0 {
reg = <0>;
label = "status-led";
color = <LED_COLOR_ID_GREEN>;
};
led@1 {
reg = <1>;
label = "error-led";
color = <LED_COLOR_ID_RED>;
};
};
/* Senzor pe I²C */
&i2c1 {
status = "okay";
clock-frequency = <400000>; /* 400 kHz Fast Mode */
temperature-sensor@48 {
compatible = "ti,tmp102";
reg = <0x48>; /* Adresa I²C */
interrupt-parent = <&gpio4>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
};
};
};
15.3 Platform driver complet¶
/* myplatdrv.c — platform driver care accesează registre MMIO */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
MODULE_LICENSE("GPL");
struct my_led_dev {
void __iomem *regs; /* Registre mapate în memorie virtuală */
struct clk *clk;
int irq;
int nr_leds;
};
/* === Probe: apelat când DT match-ul reușește === */
static int my_led_probe(struct platform_device *pdev)
{
struct my_led_dev *led;
struct resource *res;
int ret;
/* 1. Alocă structura privată (device-managed = eliberare automată) */
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
if (!led)
return -ENOMEM;
/* 2. Obține și mapează zona de registre MMIO */
led->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(led->regs))
return PTR_ERR(led->regs);
dev_info(&pdev->dev, "MMIO registers mapped at %pR\n", res);
/* 3. Obține ceasul și activează-l */
led->clk = devm_clk_get(&pdev->dev, "timer");
if (IS_ERR(led->clk))
return PTR_ERR(led->clk);
ret = clk_prepare_enable(led->clk);
if (ret)
return ret;
/* 4. Obține linia de întrerupere */
led->irq = platform_get_irq(pdev, 0);
if (led->irq < 0)
return led->irq;
/* 5. Înregistrează handler IRQ (device-managed) */
ret = devm_request_irq(&pdev->dev, led->irq, my_led_irq_handler,
0, dev_name(&pdev->dev), led);
if (ret)
return ret;
/* 6. Citește proprietăți din Device Tree */
led->nr_leds = of_get_child_count(pdev->dev.of_node);
dev_info(&pdev->dev, "Found %d LEDs\n", led->nr_leds);
/* 7. Inițializează hardware-ul */
writel(0x01, led->regs + 0x00); /* Enable register */
/* 8. Salvează pointer-ul privat */
platform_set_drvdata(pdev, led);
dev_info(&pdev->dev, "Probed successfully, IRQ=%d\n", led->irq);
return 0;
}
/* === Remove: apelat la descărcare modul sau unbind === */
static void my_led_remove(struct platform_device *pdev)
{
struct my_led_dev *led = platform_get_drvdata(pdev);
writel(0x00, led->regs + 0x00); /* Disable hardware */
clk_disable_unprepare(led->clk);
dev_info(&pdev->dev, "Removed\n");
/* Resursele devm_* se eliberează automat */
}
/* === Tabela de potrivire Device Tree === */
static const struct of_device_id my_led_of_match[] = {
{ .compatible = "mycompany,led-ctrl-v2" },
{ } /* Terminator obligatoriu */
};
MODULE_DEVICE_TABLE(of, my_led_of_match);
/* === Structura platform driver === */
static struct platform_driver my_led_driver = {
.probe = my_led_probe,
.remove = my_led_remove,
.driver = {
.name = "my-led-ctrl",
.of_match_table = my_led_of_match,
},
};
/* Macro care generează automat module_init/exit: */
module_platform_driver(my_led_driver);
15.4 Resurse device-managed (devm_*)¶
/* Prefixul devm_ = „device managed" — resursa se eliberează
* automat când dispozitivul este dezlegat de driver (remove).
* Elimină nevoia de cleanup manual și reduce bug-urile de memory leak. */
/* Memorie: */
ptr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
ptr = devm_kcalloc(&pdev->dev, n, size, GFP_KERNEL);
/* I/O mapping: */
base = devm_ioremap_resource(&pdev->dev, resource);
base = devm_platform_ioremap_resource(pdev, index);
/* Ceas: */
clk = devm_clk_get(&pdev->dev, "name");
devm_clk_get_enabled(&pdev->dev, "name"); /* get + prepare + enable */
/* IRQ: */
devm_request_irq(&pdev->dev, irq, handler, flags, name, data);
devm_request_threaded_irq(&pdev->dev, irq, hard, thread, flags, name, data);
/* GPIO: */
gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);
/* Regulator: */
reg = devm_regulator_get(&pdev->dev, "vcc");
/* PWM: */
pwm = devm_pwm_get(&pdev->dev, NULL);
/* Regulă: folosește MEREU varianta devm_ dacă există.
* Singurul caz de cleanup manual e oprirea hardware-ului în remove(). */
16. Subsistemul I²C — driver client complet¶
16.1 Arhitectura subsistemului I²C¶
Userspace (/dev/i2c-N, i2c-tools)
│
┌────▼──────────────────────────┐
│ I²C Core (drivers/i2c/i2c-core-*) │
│ │
│ Gestionează: adaptere, clienți, │
│ matching DT, transfer API │
└───┬──────────────────┬────────┘
│ │
┌────▼─────┐ ┌────▼──────┐
│ Adapter │ │ Client │
│ Driver │ │ Driver │
│ │ │ │
│ i2c-stm32│ │ tmp102 │ (driverul nostru)
│ i2c-bcm │ │ sht3x │
│ i2c-omap │ │ mpu6050 │
│ │ │ │
│ Controlează│ │ Controlează│
│ hardware-ul│ │ un chip │
│ I²C (SCL, │ │ slave pe │
│ SDA) │ │ bus-ul I²C │
└───────────┘ └────────────┘
16.2 Driver I²C client complet — senzor de temperatură¶
/* my_temp_sensor.c — driver I²C pentru senzor de temperatură */
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
MODULE_LICENSE("GPL");
#define TEMP_REG 0x00 /* Registrul de temperatură (16 biți) */
#define CONFIG_REG 0x01 /* Registrul de configurare */
struct my_temp_data {
struct i2c_client *client;
struct mutex lock;
};
/* Citire temperatură din senzor */
static int my_temp_read_temp(struct my_temp_data *data, long *temp)
{
int ret;
s16 raw;
mutex_lock(&data->lock);
ret = i2c_smbus_read_word_swapped(data->client, TEMP_REG);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
mutex_unlock(&data->lock);
/* Conversie: raw este în format Q8.4 (12 biți, rezoluție 0.0625°C) */
raw = (s16)ret >> 4;
*temp = raw * 625 / 10; /* mili-grade Celsius */
return 0;
}
/* Callback hwmon — citire valoare */
static int my_temp_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct my_temp_data *data = dev_get_drvdata(dev);
if (type != hwmon_temp || attr != hwmon_temp_input)
return -EOPNOTSUPP;
return my_temp_read_temp(data, val);
}
/* Callback hwmon — ce atribute sunt vizibile */
static umode_t my_temp_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type == hwmon_temp && attr == hwmon_temp_input)
return 0444; /* Read-only */
return 0;
}
static const struct hwmon_channel_info * const my_temp_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
static const struct hwmon_ops my_temp_hwmon_ops = {
.is_visible = my_temp_hwmon_is_visible,
.read = my_temp_hwmon_read,
};
static const struct hwmon_chip_info my_temp_chip_info = {
.ops = &my_temp_hwmon_ops,
.info = my_temp_info,
};
/* === Probe === */
static int my_temp_probe(struct i2c_client *client)
{
struct my_temp_data *data;
struct device *hwmon_dev;
int config;
/* Verifică funcționalitatea adapterului I²C */
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
mutex_init(&data->lock);
/* Configurare senzor (rezoluție 12 biți) */
config = i2c_smbus_read_word_swapped(client, CONFIG_REG);
if (config < 0)
return config;
config |= (0x3 << 5); /* Rezoluție maximă */
i2c_smbus_write_word_swapped(client, CONFIG_REG, config);
/* Înregistrare în subsistemul hwmon */
hwmon_dev = devm_hwmon_device_register_with_info(
&client->dev, "my_temp", data,
&my_temp_chip_info, NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
dev_info(&client->dev, "Temperature sensor initialized\n");
return 0;
}
/* Tabela de potrivire */
static const struct of_device_id my_temp_of_match[] = {
{ .compatible = "mycompany,temp-sensor" },
{ }
};
MODULE_DEVICE_TABLE(of, my_temp_of_match);
static const struct i2c_device_id my_temp_id[] = {
{ "my-temp-sensor", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, my_temp_id);
static struct i2c_driver my_temp_driver = {
.driver = {
.name = "my-temp-sensor",
.of_match_table = my_temp_of_match,
},
.probe = my_temp_probe,
.id_table = my_temp_id,
};
module_i2c_driver(my_temp_driver);
# Verificare din userspace:
cat /sys/class/hwmon/hwmon*/temp1_input
# 25375 (= 25.375 °C)
# Sau cu i2c-tools (debug):
sudo i2cdetect -y 1 # Scanează bus I²C 1
sudo i2cget -y 1 0x48 0x00 w # Citește registrul de temperatură
17. Subsistemul SPI¶
17.1 Structura driverului SPI¶
/* my_spi_device.c — driver SPI pentru un ADC extern */
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
MODULE_LICENSE("GPL");
struct my_adc_data {
struct spi_device *spi;
struct mutex lock;
};
/* Citire ADC prin transfer SPI */
static int my_adc_read_channel(struct my_adc_data *adc, int channel, int *value)
{
u8 tx_buf[3] = { 0x01, (0x80 | (channel << 4)), 0x00 };
u8 rx_buf[3] = { 0 };
struct spi_transfer xfer = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = 3,
.speed_hz = 1000000, /* 1 MHz pentru acest transfer */
};
int ret;
mutex_lock(&adc->lock);
ret = spi_sync_transfer(adc->spi, &xfer, 1);
mutex_unlock(&adc->lock);
if (ret)
return ret;
*value = ((rx_buf[1] & 0x03) << 8) | rx_buf[2];
return 0;
}
static int my_adc_probe(struct spi_device *spi)
{
struct my_adc_data *adc;
/* Configurare SPI */
spi->mode = SPI_MODE_0; /* CPOL=0, CPHA=0 */
spi->bits_per_word = 8;
spi->max_speed_hz = 2000000; /* 2 MHz max */
int ret = spi_setup(spi);
if (ret)
return ret;
adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL);
if (!adc)
return -ENOMEM;
adc->spi = spi;
mutex_init(&adc->lock);
spi_set_drvdata(spi, adc);
dev_info(&spi->dev, "ADC probed, mode=%d, speed=%d Hz\n",
spi->mode, spi->max_speed_hz);
return 0;
}
static const struct of_device_id my_adc_of_match[] = {
{ .compatible = "mycompany,adc-3ch" },
{ }
};
MODULE_DEVICE_TABLE(of, my_adc_of_match);
static struct spi_driver my_adc_driver = {
.driver = {
.name = "my-adc",
.of_match_table = my_adc_of_match,
},
.probe = my_adc_probe,
};
module_spi_driver(my_adc_driver);
18. Subsistemul GPIO și pinctrl¶
18.1 API-ul GPIO bazat pe descriptori (gpiod)¶
/* API-ul modern (gpiod) — înlocuiește API-ul vechi bazat pe numere */
#include <linux/gpio/consumer.h>
/* În probe(): */
struct gpio_desc *led_gpio;
struct gpio_desc *btn_gpio;
/* Obține GPIO din Device Tree (proprietatea "led-gpios") */
led_gpio = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
if (IS_ERR(led_gpio))
return PTR_ERR(led_gpio);
/* Obține GPIO de intrare (proprietatea "button-gpios") */
btn_gpio = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN);
if (IS_ERR(btn_gpio))
return PTR_ERR(btn_gpio);
/* Control: */
gpiod_set_value(led_gpio, 1); /* Aprinde LED */
gpiod_set_value(led_gpio, 0); /* Stinge LED */
int state = gpiod_get_value(btn_gpio); /* Citește buton */
/* Obține IRQ de la GPIO: */
int irq = gpiod_to_irq(btn_gpio);
devm_request_threaded_irq(&pdev->dev, irq, NULL, my_btn_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"my-button", data);
/* Device Tree corespunzător: */
my-device {
compatible = "mycompany,my-device";
led-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
button-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
};
18.2 Subsistemul LED¶
/* Modalitatea corectă de a expune LED-uri: subsistemul LED */
#include <linux/leds.h>
struct my_led {
struct led_classdev cdev;
struct gpio_desc *gpio;
};
static void my_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct my_led *led = container_of(cdev, struct my_led, cdev);
gpiod_set_value(led->gpio, brightness != LED_OFF);
}
/* În probe(): */
led->cdev.name = "myboard:green:status";
led->cdev.brightness_set = my_led_set;
led->cdev.max_brightness = 1;
led->cdev.default_trigger = "heartbeat"; /* Pulsează automat */
ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
/* Rezultat în sysfs:
* /sys/class/leds/myboard:green:status/brightness
* /sys/class/leds/myboard:green:status/trigger
* echo 1 > /sys/class/leds/myboard:green:status/brightness
* echo timer > /sys/class/leds/myboard:green:status/trigger */
19. Stiva de rețea Linux (Networking Stack)¶
19.1 Arhitectura stivei de rețea¶
┌────────────────────────────────────────────────────────┐
│ APLICAȚIE: send(), recv(), socket() │
├────────────────────────────────────────────────────────┤
│ SOCKET LAYER │
│ Abstracție BSD sockets: socket(), bind(), connect(), │
│ listen(), accept(), send(), recv(), select(), poll() │
│ │
│ struct socket → struct sock │
│ AF_INET (IPv4), AF_INET6 (IPv6), AF_PACKET (raw), │
│ AF_UNIX (local), AF_NETLINK (kernel↔userspace) │
├────────────────────────────────────────────────────────┤
│ TRANSPORT LAYER │
│ TCP (net/ipv4/tcp*.c): conexiune, fiabilitate, flow ctrl│
│ UDP (net/ipv4/udp.c): datagrame fără conexiune │
│ SCTP, DCCP, QUIC (userspace), etc. │
├────────────────────────────────────────────────────────┤
│ NETWORK LAYER │
│ IPv4 (net/ipv4/): rutare, fragmentare, ICMP │
│ IPv6 (net/ipv6/): similar, extensii │
│ Netfilter / nftables: firewall, NAT, packet mangling │
│ Routing: FIB (Forwarding Information Base), policy routing│
├────────────────────────────────────────────────────────┤
│ NEIGHBOUR SUBSYSTEM │
│ ARP (IPv4), NDP (IPv6): rezoluție adrese IP → MAC │
├────────────────────────────────────────────────────────┤
│ LINK LAYER / QUEUEING DISCIPLINE (qdisc) │
│ tc (traffic control): HTB, TBF, SFQ, fq_codel │
│ Scheduling-ul pachetelor de ieșire │
├────────────────────────────────────────────────────────┤
│ NETWORK DEVICE ABSTRACTION (struct net_device) │
│ dev_queue_xmit(), netif_receive_skb() │
│ NAPI (New API): polling-based receive (anti-IRQ storm) │
├────────────────────────────────────────────────────────┤
│ NETWORK DRIVER (e1000e, igb, ixgbe, mlx5, virtio-net) │
│ Controlează hardware-ul NIC │
│ DMA ring buffers, descriptor tables │
├────────────────────────────────────────────────────────┤
│ HARDWARE (NIC — Network Interface Card) │
└────────────────────────────────────────────────────────┘
19.2 struct sk_buff — pachetul de rețea¶
sk_buff (socket buffer) este structura centrală care reprezintă un pachet de rețea în kernel:
/* Structura sk_buff (puternic simplificată): */
struct sk_buff {
/* Pointeri de navigare prin lista de skb-uri */
struct sk_buff *next, *prev;
/* Timestampuri */
ktime_t tstamp;
/* Dispozitivul de rețea asociat */
struct net_device *dev;
/* Pointeri la headere protocoale */
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
} transport_header;
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
} network_header;
unsigned char *mac_header;
/* Pointeri la date */
unsigned char *head; /* Începutul buffer-ului alocat */
unsigned char *data; /* Începutul datelor curente */
unsigned char *tail; /* Sfârșitul datelor curente */
unsigned char *end; /* Sfârșitul buffer-ului alocat */
unsigned int len; /* Lungimea datelor */
unsigned int data_len; /* Lungime date în fragmente */
__u16 protocol; /* Protocol (ETH_P_IP, ETH_P_ARP, ...) */
/* ... multe alte câmpuri ... */
};
/* Manipulare skb: */
struct sk_buff *skb = alloc_skb(size, GFP_KERNEL);
/* Adaugă date la coadă: */
unsigned char *p = skb_put(skb, len); /* Avansează tail cu len */
memcpy(p, data, len);
/* Adaugă header la început: */
unsigned char *h = skb_push(skb, hdr_len); /* Retrage data cu hdr_len */
/* Elimină header de la început: */
skb_pull(skb, hdr_len); /* Avansează data cu hdr_len */
/* Rezervă spațiu la începutul bufferului: */
skb_reserve(skb, NET_IP_ALIGN + ETH_HLEN + sizeof(struct iphdr));
/*
* Layout buffer:
* head ─────► [headroom ][ DATA ][ tailroom ]◄──── end
* (reserve) ↑ ↑
* data tail
* ├──push──► ◄──put──┤
*/
/* Eliberare: */
kfree_skb(skb); /* Cu contorizare referințe */
consume_skb(skb); /* Varianta „normală" (non-drop) */
19.3 NAPI — recepție eficientă¶
/* NAPI (New API) — evită interrupt storms la trafic intens
* Principiu: la primul pachet, IRQ → treci în mod polling
* Procesezi pachete în batch (budget), apoi re-activezi IRQ */
/* În probe(): */
netif_napi_add(netdev, &priv->napi, my_napi_poll);
napi_enable(&priv->napi);
/* IRQ handler (top-half): */
static irqreturn_t my_net_irq(int irq, void *dev_id)
{
struct my_net_priv *priv = dev_id;
/* Dezactivează întreruperile NIC */
writel(0, priv->regs + INT_ENABLE_REG);
/* Programează NAPI polling */
napi_schedule(&priv->napi);
return IRQ_HANDLED;
}
/* NAPI poll (bottom-half — softirq context): */
static int my_napi_poll(struct napi_struct *napi, int budget)
{
struct my_net_priv *priv = container_of(napi, struct my_net_priv, napi);
int processed = 0;
while (processed < budget) {
/* Verifică dacă mai sunt pachete în DMA ring */
if (!my_rx_pending(priv))
break;
/* Extrage pachetul din DMA ring buffer */
struct sk_buff *skb = my_rx_packet(priv);
if (!skb)
break;
/* Trimite pachetul în sus, în stiva de rețea */
skb->protocol = eth_type_trans(skb, priv->netdev);
napi_gro_receive(napi, skb); /* GRO = Generic Receive Offload */
processed++;
}
/* Dacă am procesat mai puțin decât budget-ul,
* am terminat — ieșim din polling, re-activăm IRQ */
if (processed < budget) {
napi_complete_done(napi, processed);
writel(INT_RX_MASK, priv->regs + INT_ENABLE_REG);
}
return processed;
}
19.4 Netfilter — hookuri de filtrare¶
/* Netfilter oferă 5 hook points în calea pachetului: */
/*
* Pachet incoming:
* NF_INET_PRE_ROUTING → Routing decision
* │
* ┌────────────┼────────────┐
* │ (local) │ (forward) │
* ▼ │ ▼
* NF_INET_LOCAL_IN │ NF_INET_FORWARD
* │ │ │
* ▼ │ ▼
* Local process │ NF_INET_POST_ROUTING → Out
* │ │
* ▼ │
* NF_INET_LOCAL_OUT │
* │ │
* └────────────┘
* │
* NF_INET_POST_ROUTING → Out
*/
/* Înregistrare hook netfilter: */
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
static unsigned int my_nf_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct iphdr *iph;
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb);
/* Exemplu: loghează și blochează pachete de la o adresă specifică */
if (iph->saddr == cpu_to_be32(0xC0A80164)) { /* 192.168.1.100 */
pr_info("Blocked packet from 192.168.1.100\n");
return NF_DROP;
}
return NF_ACCEPT;
}
static const struct nf_hook_ops my_nf_ops = {
.hook = my_nf_hook,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
};
/* La init: */
nf_register_net_hook(&init_net, &my_nf_ops);
/* La exit: */
nf_unregister_net_hook(&init_net, &my_nf_ops);
20. Debugging și tracing în kernel¶
20.1 printk și niveluri de logging¶
/* printk — funcția fundamentală de logging în kernel */
/* Niveluri (de la cel mai critic la cel mai informativ): */
pr_emerg("System is unusable\n"); /* KERN_EMERG = 0 */
pr_alert("Action must be taken\n"); /* KERN_ALERT = 1 */
pr_crit("Critical condition\n"); /* KERN_CRIT = 2 */
pr_err("Error condition\n"); /* KERN_ERR = 3 */
pr_warn("Warning condition\n"); /* KERN_WARNING = 4 */
pr_notice("Normal but noteworthy\n"); /* KERN_NOTICE = 5 */
pr_info("Informational\n"); /* KERN_INFO = 6 */
pr_debug("Debug message\n"); /* KERN_DEBUG = 7 — doar cu DEBUG definit */
/* dev_* — variantele preferate în drivere (include device name): */
dev_err(&pdev->dev, "Failed: %d\n", ret);
/* Output: "my-device 40010000.led-ctrl: Failed: -22" */
/* Rate-limited (nu spammează log-ul): */
pr_info_ratelimited("Event occurred\n"); /* Max 10 mesaje la 5 sec */
/* Print o singură dată: */
pr_warn_once("This prints only once\n");
/* Dynamic debug (activare/dezactivare per fișier/funcție la runtime): */
/* echo 'file my_driver.c +p' > /sys/kernel/debug/dynamic_debug/control */
/* echo 'func my_probe +p' > /sys/kernel/debug/dynamic_debug/control */
# Vizualizare mesaje kernel:
dmesg # Toate mesajele
dmesg -T # Cu timestamps human-readable
dmesg -w # Urmărire live (ca tail -f)
dmesg -l err,warn # Doar erori și warnings
journalctl -k # Pe sisteme cu systemd
20.2 /proc și /sys pentru debugging¶
# === Informații hardware și kernel ===
cat /proc/cpuinfo # Informații CPU
cat /proc/meminfo # Starea memoriei
cat /proc/interrupts # Contori IRQ per CPU
cat /proc/iomem # Harta memoriei fizice (MMIO)
cat /proc/ioports # Porturi I/O (x86)
cat /proc/modules # Module încărcate
cat /proc/kallsyms # Toate simbolurile kernel (+ adrese)
cat /proc/slabinfo # Statistici alocator slab
# === Per-proces ===
cat /proc/$$/maps # Harta memoriei virtuale
cat /proc/$$/status # Stare proces (VmRSS, threads, signals)
cat /proc/$$/fd/ # File descriptori deschisi
cat /proc/$$/stack # Stiva kernel a procesului
cat /proc/$$/sched # Statistici scheduling
# === Tuning kernel ===
cat /proc/sys/vm/swappiness # Agresivitate swap (0-200)
cat /proc/sys/kernel/sched_latency_ns # Latența țintă CFS
echo 1 > /proc/sys/kernel/sysrq # Activează SysRq magic keys
20.3 Ftrace — tracing intern¶
# Ftrace este tracerul integrat în kernel (/sys/kernel/debug/tracing/)
cd /sys/kernel/debug/tracing
# === Function tracing ===
echo function > current_tracer
echo 1 > tracing_on
# ... execută operațiunea de testat ...
echo 0 > tracing_on
cat trace | head -50
# === Function graph (include duratele apelurilor) ===
echo function_graph > current_tracer
echo '*spi*' > set_ftrace_filter # Tracează doar funcțiile SPI
echo 1 > tracing_on
# ... operațiune SPI ...
echo 0 > tracing_on
cat trace
# Output:
# 2) | spi_sync() {
# 2) | __spi_sync() {
# 2) | spi_transfer_one_message() {
# 2) 5.234 us | stm32_spi_transfer_one();
# 2) 8.112 us | }
# 2) + 10.387 us | }
# 2) + 12.456 us | }
# === Event tracing (tracepoints) ===
echo 1 > events/irq/irq_handler_entry/enable
echo 1 > events/sched/sched_switch/enable
echo 1 > tracing_on
# ...
cat trace
# === Trace doar un proces ===
echo $PID > set_ftrace_pid
20.4 perf — profiling hardware și software¶
# Profiling CPU (sampling):
perf record -g ./my_program # Înregistrează cu call graph
perf report # Vizualizare interactivă
# Statistici hardware:
perf stat ./my_program
# Performance counter stats:
# 1,523,456,789 cycles
# 456,789,012 instructions # 0.30 IPC
# 12,345,678 cache-misses # 5.23% of cache references
# 1,234,567 branch-misses # 2.1% of all branches
# 0.456 seconds time elapsed
# Trace evenimente kernel:
perf trace -e 'sched:*' -p $PID # Trace scheduling events
perf trace -e 'syscalls:*' ls # Trace syscalls ale 'ls'
# Flame graph (vizualizare profil):
perf record -F 99 -g ./my_program
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
20.5 eBPF — programare dinamică a kernel-ului¶
# eBPF (extended Berkeley Packet Filter) permite atașarea de programe
# la puncte din kernel fără recompilare, cu verificare de siguranță.
# Instrumente populare bazate pe eBPF:
# bcc (BPF Compiler Collection):
sudo opensnoop-bpfcc # Tracează toate open() din sistem
sudo execsnoop-bpfcc # Tracează toate exec() din sistem
sudo biosnoop-bpfcc # Tracează operații block I/O
sudo tcpconnect-bpfcc # Tracează conexiuni TCP noi
sudo funccount-bpfcc 'vfs_*' # Numără apeluri funcții VFS
# bpftrace (limbaj de scripting BPF):
# Histogramă latențe read():
sudo bpftrace -e '
kprobe:vfs_read { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ {
@us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'
# Trace deschideri de fișiere:
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat {
printf("%s opened %s\n", comm, str(args.filename));
}'
20.6 KGDB — debugging interactiv al kernel-ului¶
# Configurare kernel: CONFIG_KGDB=y, CONFIG_KGDB_SERIAL_CONSOLE=y
# Pe mașina target (kernel command line):
# kgdboc=ttyS0,115200 kgdbwait
# Pe mașina host:
gdb vmlinux
(gdb) target remote /dev/ttyS0
(gdb) break my_driver_probe
(gdb) continue
# ... kernel-ul se oprește la breakpoint ...
(gdb) print *pdev
(gdb) bt # backtrace
(gdb) next
(gdb) continue
# Cu QEMU (mult mai convenabil):
qemu-system-x86_64 -kernel bzImage -s -S ...
# Pe alt terminal:
gdb vmlinux
(gdb) target remote :1234
20.7 KASAN, KMSAN, KCSAN — sanitizere kernel¶
# CONFIG_KASAN=y — Kernel Address Sanitizer
# Detectează: use-after-free, buffer overflow, stack overflow
# Overhead: ~2-3x încetinire, ~2x memorie suplimentară
# CONFIG_KMSAN=y — Kernel Memory Sanitizer
# Detectează: utilizarea memoriei neinițializate
# CONFIG_KCSAN=y — Kernel Concurrency Sanitizer
# Detectează: data races (accese concurente fără sincronizare)
# CONFIG_UBSAN=y — Undefined Behavior Sanitizer
# Detectează: signed overflow, alignment issues, null ptr deref
# CONFIG_LOCKDEP=y — Lock Dependency Validator
# Detectează: deadlocks potențiale (analizează ordinea lock-urilor)
# Raportează: circular dependencies, lock ordering violations
# Exemplu output KASAN:
# BUG: KASAN: slab-out-of-bounds in my_function+0x42/0x100
# Write of size 4 at addr ffff888012345678 by task insmod/1234
# ...
# Allocated by task insmod/1234:
# kmalloc+0x12/0x20
# my_probe+0x34/0x200
21. Securitatea kernel-ului¶
21.1 Modelul de securitate Linux¶
┌─────────────────────────────────────────────────────────┐
│ Mecanisme de securitate │
│ │
│ ┌──────────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ DAC │ │ Capabilities │ │ Namespaces │ │
│ │ (rwxrwxrwx) │ │ (granulare) │ │ (izolare) │ │
│ │ │ │ │ │ │ │
│ │ Permisiuni │ │ CAP_NET_ADMIN │ │ PID ns │ │
│ │ clasice UNIX │ │ CAP_SYS_ADMIN │ │ Mount ns │ │
│ │ UID/GID │ │ CAP_NET_RAW │ │ Network ns │ │
│ └──────────────┘ └───────────────┘ │ User ns │ │
│ │ UTS ns │ │
│ ┌──────────────┐ ┌───────────────┐ │ IPC ns │ │
│ │ LSM │ │ seccomp-bpf │ │ Cgroup ns │ │
│ │ (Mandatory │ │ (syscall │ └──────────────┘ │
│ │ Access Ctrl)│ │ filtering) │ │
│ │ │ │ │ ┌──────────────┐ │
│ │ SELinux │ │ Restricțion- │ │ cgroups │ │
│ │ AppArmor │ │ ează syscalls │ │ (resource │ │
│ │ SMACK │ │ permise per │ │ limits) │ │
│ │ Landlock │ │ proces │ │ CPU, mem, │ │
│ └──────────────┘ └───────────────┘ │ I/O, net │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
21.2 Capabilities¶
/* Capabilities înlocuiesc bitul setuid-root cu permisiuni granulare */
#include <linux/capability.h>
/* Verificare în kernel: */
if (!capable(CAP_NET_ADMIN)) {
return -EPERM; /* Procesul nu are dreptul */
}
/* Capabilities importante: */
/* CAP_SYS_ADMIN — supercapability (mount, swapon, ioctl, syslog...) */
/* CAP_NET_ADMIN — configurare rețea (ifconfig, iptables, routes) */
/* CAP_NET_RAW — raw sockets (ping, tcpdump) */
/* CAP_SYS_RAWIO — acces I/O direct (ioperm, iopl) */
/* CAP_DAC_OVERRIDE— ignoră permisiunile fișier */
/* CAP_SYS_PTRACE — trace orice proces (gdb root) */
/* CAP_SYS_MODULE — încărcare module kernel */
# Din userspace:
# Setează capability pe un executabil:
sudo setcap cap_net_raw+ep /usr/bin/ping
# Acum ping funcționează fără setuid-root
# Verifică capabilities:
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
21.3 Seccomp-BPF — sandboxing syscalls¶
/* seccomp restricționează ce syscalls poate face un proces */
#include <linux/seccomp.h>
#include <linux/bpf.h>
/* Modul STRICT: permite doar read(), write(), exit(), sigreturn() */
prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
/* Modul FILTER: program BPF care decide per syscall */
/* Exemplu simplificat — un filter care blochează execve(): */
struct sock_filter filter[] = {
/* Load syscall number */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
/* If syscall == __NR_execve, kill */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
/* Otherwise, allow */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter,
};
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); /* Obligatoriu */
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
21.4 Namespaces — izolarea resurselor¶
# Namespaces sunt baza containerelor (Docker, Podman, LXC)
# Tipuri de namespaces:
# PID — proces IDs izolate (PID 1 al containerului)
# NET — stiva de rețea izolată (interfețe, IP-uri, rute proprii)
# MNT — mount points izolate (filesystem privat)
# UTS — hostname izolat
# IPC — semafoare, cozi de mesaje izolate
# USER — UID/GID mapping (root în container != root pe host)
# CGROUP — vizualizare cgroup izolată
# TIME — (kernel 5.6+) ceas sistem izolat
# Creare namespace din shell:
sudo unshare --pid --fork --mount-proc bash
# Acum suntem într-un PID namespace nou:
ps aux # Vedem doar procesele din namespace-ul nostru
# PID 1 = bash-ul nostru
# Din kernel, namespaces se creează cu clone() flags:
# clone(CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET, ...)
21.5 Cgroups — limitarea resurselor¶
# cgroups v2 (unificat, montat pe /sys/fs/cgroup)
# Limitare memorie:
mkdir /sys/fs/cgroup/mygroup
echo "100M" > /sys/fs/cgroup/mygroup/memory.max
echo "80M" > /sys/fs/cgroup/mygroup/memory.high # Throttling
echo $PID > /sys/fs/cgroup/mygroup/cgroup.procs # Adaugă proces
# Limitare CPU (max 50%):
echo "50000 100000" > /sys/fs/cgroup/mygroup/cpu.max
# Limitare I/O:
echo "8:0 riops=1000 wiops=500" > /sys/fs/cgroup/mygroup/io.max
# Monitorizare:
cat /sys/fs/cgroup/mygroup/memory.current # Consum curent
cat /sys/fs/cgroup/mygroup/cpu.stat # Statistici CPU
21.6 LSM — SELinux și AppArmor¶
# SELinux (Red Hat, Fedora, Android):
# Fiecare proces și fișier are un context de securitate (label)
# Politicile definesc ce context poate accesa ce alt context
ls -Z /usr/bin/httpd
# system_u:object_r:httpd_exec_t:s0
# Verificare status:
getenforce # Enforcing, Permissive, Disabled
sestatus # Detalii
# AppArmor (Ubuntu, SUSE):
# Profile per-aplicație care restricționează accesul la fișiere, rețea
sudo aa-status # Profile active
cat /etc/apparmor.d/usr.sbin.nginx
# Landlock (kernel 5.13+, non-privilegiat):
# Procese obișnuite își pot restricționa propria linie accesul la FS
# Fără a fi root, fără setarea unui profile de administrator
22. Virtualizare și containere¶
22.1 KVM — Kernel-based Virtual Machine¶
┌─────────────────────────────────────────────────────┐
│ Host (Linux kernel cu KVM) │
│ │
│ ┌────────────────────────────────┐ │
│ │ QEMU (userspace) │ │
│ │ Emulează: dispozitive virtuale │ │
│ │ (disk virtio, net virtio, │ │
│ │ display VGA, USB, serial) │ │
│ └─────────────┬──────────────────┘ │
│ │ ioctl(KVM_RUN) │
│ ┌─────────────▼──────────────────┐ │
│ │ KVM (kernel module) │ │
│ │ │ │
│ │ Gestionează: │ │
│ │ - vCPU (VMCS/VMCB) │ │
│ │ - Memorie (EPT/NPT) │ │
│ │ - Întreruperi virtuale │ │
│ │ - VM exits │ │
│ └─────────────┬──────────────────┘ │
│ │ Hardware (VT-x / AMD-V) │
│ ┌─────────────▼──────────────────┐ │
│ │ CPU Hardware │ │
│ │ VMX root mode (host) │ │
│ │ VMX non-root mode (guest) │ │
│ │ │ │
│ │ VM Entry: host → guest │ │
│ │ VM Exit: guest → host │ │
│ │ (la I/O, instrucțiuni │ │
│ │ privilegiate, page faults) │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
ARM echivalent:
- EL2 (Hypervisor mode) în loc de VMX root
- Stage 2 page tables în loc de EPT
22.2 Virtio — dispozitive paravirtualizate¶
/* virtio elimină emularea hardware realistă (lentă)
* Guest-ul știe că rulează în VM și cooperează cu host-ul
* prin cozi de mesaje partajate (virtqueues) */
/* Structura unei virtqueue:
*
* ┌─────────────────────────────────┐
* │ Descriptor Table │ Pointeri la buffere de date
* │ [0] addr=0x1000, len=4096, next │
* │ [1] addr=0x2000, len=256 │
* │ ... │
* ├─────────────────────────────────┤
* │ Available Ring │ Guest → Host: „am adăugat buffere"
* │ idx=5, ring=[0,1,3,2,4,...] │
* ├─────────────────────────────────┤
* │ Used Ring │ Host → Guest: „am procesat buffere"
* │ idx=3, ring=[(0,4096),(1,256)] │
* └─────────────────────────────────┘
*
* Notificare:
* Guest → Host: scriere la registru MMIO (vring kick) sau VMCALL
* Host → Guest: injectare întrerupere virtuală (vIRQ)
*/
/* Dispozitive virtio standard:
* virtio-blk — block device (disk virtual)
* virtio-net — network device
* virtio-gpu — accelerare grafică
* virtio-fs — filesystem partajat (virtiofsd)
* virtio-vsock — socket host↔guest
*/
22.3 Containere — stiva completă¶
┌─────────────────────────────────────────────────┐
│ Container Runtime (Docker, containerd, cri-o) │
│ │
│ Orchestrare de nivel înalt: │
│ - Pull/push imagini OCI │
│ - Gestionare lifecycle container │
│ - Networking (bridge, overlay) │
│ - Storage (overlayfs, volumes) │
├─────────────────────────────────────────────────┤
│ runc (OCI runtime reference implementation) │
│ │
│ Creează containerul folosind syscalls kernel: │
│ 1. clone(CLONE_NEWPID | CLONE_NEWNS | │
│ CLONE_NEWNET | CLONE_NEWUTS | │
│ CLONE_NEWIPC | CLONE_NEWUSER) │
│ 2. pivot_root() → mount overlay filesystem │
│ 3. Configurare cgroups v2 (memorie, CPU, I/O) │
│ 4. seccomp-bpf filter (restricții syscalls) │
│ 5. Setare capabilities (drop all, add necesare) │
│ 6. AppArmor/SELinux profile │
│ 7. exec() procesul containerului │
├─────────────────────────────────────────────────┤
│ Linux Kernel │
│ │
│ Mecanisme folosite: │
│ ┌────────────┐ ┌────────┐ ┌──────────────────┐ │
│ │ Namespaces │ │ Cgroups│ │ Overlay FS │ │
│ │ (izolare) │ │(limite)│ │ (layers imagine) │ │
│ └────────────┘ └────────┘ └──────────────────┘ │
│ ┌────────────┐ ┌────────┐ ┌──────────────────┐ │
│ │ seccomp │ │ LSM │ │ veth pairs │ │
│ │ (syscalls) │ │(MAC) │ │ (virtual network)│ │
│ └────────────┘ └────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────┘
22.4 OverlayFS — sistemul de fișiere al containerelor¶
# OverlayFS suprapune mai multe layere filesystem:
# Lower layers: read-only (imaginea container)
# Upper layer: read-write (modificările containerului)
# Merged: vizualizarea unificată
# Exemplu manual:
mkdir lower upper work merged
echo "from image" > lower/file.txt
mount -t overlay overlay \
-o lowerdir=lower,upperdir=upper,workdir=work \
merged/
# Citirea merge/file.txt → citește din lower/
# Scrierea în merged/ → se face în upper/ (copy-up)
echo "modified" > merged/file.txt
# Acum upper/file.txt există cu conținut modificat
# lower/file.txt rămâne neschimbat
# Docker layers:
# Image Layer 1 (base: ubuntu) ← lower
# Image Layer 2 (apt install nginx) ← lower
# Image Layer 3 (copy config) ← lower
# Container Layer (runtime changes) ← upper (read-write)
Anexe¶
A. Scheletul standard al unui driver Linux modern¶
/* Template: modern Linux device driver */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
/* Date private per-dispozitiv */
struct mydrv_data {
void __iomem *regs;
struct clk *clk;
int irq;
/* ... resurse specifice ... */
};
/* IRQ handler */
static irqreturn_t mydrv_irq(int irq, void *data)
{
struct mydrv_data *priv = data;
/* Ack + procesare minimă */
return IRQ_HANDLED;
}
/* Probe */
static int mydrv_probe(struct platform_device *pdev)
{
struct mydrv_data *priv;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
priv->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0)
return priv->irq;
ret = devm_request_irq(&pdev->dev, priv->irq, mydrv_irq,
0, dev_name(&pdev->dev), priv);
if (ret)
return ret;
platform_set_drvdata(pdev, priv);
dev_info(&pdev->dev, "Probed OK\n");
return 0;
}
static void mydrv_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "Removed\n");
}
static const struct of_device_id mydrv_of_match[] = {
{ .compatible = "vendor,my-device" },
{ }
};
MODULE_DEVICE_TABLE(of, mydrv_of_match);
static struct platform_driver mydrv_driver = {
.probe = mydrv_probe,
.remove = mydrv_remove,
.driver = {
.name = "my-driver",
.of_match_table = mydrv_of_match,
},
};
module_platform_driver(mydrv_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author Name");
MODULE_DESCRIPTION("My Platform Device Driver");
B. Checklist dezvoltare driver kernel¶
□ Folosește devm_* pentru TOATE resursele (memorie, IRQ, clk, GPIO, ioremap)
□ Verifică TOATE valorile de retur (niciodată ignorate)
□ Folosește dev_err/dev_info (nu pr_err/pr_info) în drivere
□ Protejează datele partajate cu sincronizare adecvată
□ Respectă regulile context (nu dormi în IRQ, nu alocat GFP_KERNEL din atomic)
□ Implementează remove() care oprește hardware-ul clean
□ Folosește of_device_id pentru Device Tree matching
□ Adaugă MODULE_DEVICE_TABLE() pentru auto-loading
□ Rulează checkpatch.pl: scripts/checkpatch.pl --strict -f mydriver.c
□ Compilează cu -W: make W=1 M=drivers/mydir/
□ Testează cu KASAN activat (CONFIG_KASAN=y)
□ Testează cu lockdep activat (CONFIG_LOCKDEP=y)
□ Testează load/unload repetat (stress test lifecycle)
□ Testează cu diverse valori de CONFIG_HZ
□ Documentează bindings DT în Documentation/devicetree/bindings/
C. Referință rapidă — coduri de eroare kernel¶
| Cod | Valoare | Semnificație |
|---|---|---|
-EPERM |
-1 | Operation not permitted |
-ENOENT |
-2 | No such file or directory |
-EIO |
-5 | I/O error |
-ENOMEM |
-12 | Out of memory |
-EACCES |
-13 | Permission denied |
-EFAULT |
-14 | Bad address (invalid userspace pointer) |
-EBUSY |
-16 | Device or resource busy |
-ENODEV |
-19 | No such device |
-EINVAL |
-22 | Invalid argument |
-ENOSPC |
-28 | No space left on device |
-ERANGE |
-34 | Math result not representable |
-ENOSYS |
-38 | Function not implemented |
-ENODATA |
-61 | No data available |
-EPROTO |
-71 | Protocol error |
-EOPNOTSUPP |
-95 | Operation not supported |
-ETIMEDOUT |
-110 | Connection timed out |
-EINTR |
-4 | Interrupted system call |
-EAGAIN |
-11 | Try again (non-blocking would block) |
-ENOTTY |
-25 | Inappropriate ioctl for device |
-EPROBE_DEFER |
-517 | Driver requests deferred probing |
D. Glosar Partea II¶
| Termen | Definiție |
|---|---|
| LKM | Loadable Kernel Module — cod kernel încărcabil dinamic |
| DT / DTS / DTB | Device Tree / Source / Blob — descriere hardware |
| MMIO | Memory-Mapped I/O — registre periferice accesate ca memorie |
| devm_* | Device-managed — resurse cu eliberare automată |
| probe / remove | Funcțiile lifecycle ale unui driver |
| cdev | Character device — acces pe stream de bytes |
| blk-mq | Block Multi-Queue — subsistem block I/O modern |
| sk_buff (skb) | Socket buffer — structura unui pachet de rețea |
| NAPI | New API — recepție rețea eficientă prin polling |
| netfilter | Framework de filtrare pachete (firewall kernel) |
| ftrace | Function tracer integrat în kernel |
| eBPF | Extended BPF — VM sigură pentru programe în kernel |
| KASAN | Kernel Address Sanitizer |
| KVM | Kernel-based Virtual Machine |
| virtio | Standard paravirtualizare I/O |
| namespace | Izolare resurse kernel (PID, net, mount, user) |
| cgroup | Control Group — limitare și contabilizare resurse |
| seccomp | Secure Computing — filtrare syscalls |
| LSM | Linux Security Module (SELinux, AppArmor) |
| overlayfs | Filesystem cu layere (baza imaginilor container) |
Partea II din cursul avansat de Sisteme de Operare. Împreună cu Partea I, cursul acoperă arhitectura completă a kernel-ului Linux: de la boot și scheduling, până la drivere hardware, networking, debugging și securitate.