Laborator 9 - Drivere de sisteme de fișiere (Linux) partea 2

Obiectivele laboratorului

  • Sistematizarea noțiunilor de inode, file și dentry.
  • Dobândirea de cunoștințe despre implementarea suportului pentru lucru cu fișiere obișnuite și directoare în VFS (Virtual File System).
  • Obținerea de cunoștințe despre implementarea internă a unui sistem de fișiere.

Cuvinte cheie

  • VFS
  • inode
  • file
  • dentry

Materiale ajutătoare

Inode-ul

Inode-ul este o componentă esențială a unui sistem de fișiere UNIX ( ext4, reiserfs) și, în același timp, o componentă importantă a VFS. Un inode este o metadată (deține informații despre informații). Un inode identifică în mod unic un fișier de pe disc și deține informații despre acesta (uid, gid, drepturi de acces, timpi de acces, pointeri la blocurile de date etc.). Un aspect important este faptul că un inode nu deține informații despre numele fișierului (acesta este reținut de structura struct dentry asociată).

Inode-ul referă un fișier de pe disc. Pentru referirea unui fișier deschis (asociat cu un descriptor de fișier din cadrul unui proces) se folosește structura struct file. Unui inode îi corespund zero sau mai multe structuri file (mai multe procese pot deschide același fișier, sau un proces poate deschide același fișier de mai multe ori).

Inode-ul există atât ca entitate VFS (în memorie), cât și ca entitate pe disc (pentru sistemele de fișiere UNIX, HFS, NTFS etc.). Inode-ul din VFS este reprezentat de structura struct inode. Ca și celelalte structuri din VFS, struct inode este o structură generică care acoperă opțiunile pentru toate tipurile de fișiere suportate, chiar și acele sisteme de fișiere care nu au o entitate pe disc asociată (cum este FAT).

Structura inode

Structura struct inode este unică pentru toate sistemele de fișiere. În general sistemele de fișiere dețin și informații particulare. Acestea sunt referite prin intermediul câmpului i_private al structurii. Convențional, structura care păstrează acele informații particulare este denumită fsname_inode_info, unde fsname reprezintă numele sistemului de fișiere. Spre exemplu, sistemele de fișiere minix și ext4 păstrează informațiile particulare în structurile struct minix_inode_info, respectiv struct ext4_inode_info.

Câteva din câmpurile importante ale structurii struct inode sunt:

  • i_sb: superblocul sistemului de fișiere de care aparține acest inode
  • i_rdev: dispozitivul pe care este montat acest sistem de fișiere
  • i_ino: numărul inode-ului (identifică în mod unic inode-ul în cadrul sistemului de fișiere)
  • i_blkbits: log2(dimensiunea blocului)
  • i_mode, i_uid, i_gid: drepturile de access, uid-ul, gid-ul
  • i_size: dimensiunea fișierului/directorului/etc. în octeți
  • i_mtime, i_atime, i_ctime: timpul de modificare, access și creare
  • i_nlink: numărul de intrări de nume care folosesc acest inode; pentru sistemele de fișiere fără link-uri (fie ele hard sau simbolice) este întotdeauna setat pe 1
  • i_blocks: numărul de blocuri folosite de fișier (toate blocurile, nu doar cele de date); sunt folosite doar de subsistemul de quota
  • i_op, i_fop: pointeri către operațiile: struct inode_operations și struct file_operations; i_mapping→a_ops conține pointer-ul către operațiile struct address_space_operations
  • i_count: contorul inode-ului care indică de câte componente kernel este folosit

Câteva funcții care pot fi utilizate în lucrul cu inode-uri:

  • new_inode: creează un nou inode, setează câmpul i_nlink pe 1 și inițializează câmpurile i_blkbits, i_sb și i_dev;
  • insert_inode_hash: adaugă inode-ul la tabela hash de inode-uri; un efect interesant al acestui apel este că inode-ul va fi scris pe disc dacă este marcat dirty;

Un inode creat cu new_inode() nu este în hash table, și în afară de cazul în care aveți motive serioase, trebuie să îl introduceți în hash table;

  • mark_inode_dirty: marchează inode-ul dirty; la un moment ulterior el va fi scris pe disc;
  • iget_locked: încarcă inode-ul cu numărul dat de pe disc, dacă nu este deja încărcat;
  • unlock_new_inode: folosit în conjuncție cu iget_locked, eliberează lock-ul de pe inode;
  • iput: anunță kernel-ul că s-a terminat de lucrat asupra inode-ului; dacă nu îl mai folosește nimeni el va fi distrus (după ce va fi scris pe disc dacă este dirty);
  • make_bad_inode: anunță kernel-ul că respectivul inode nu poate fi folosit; în general se folosește din funcția care citește inode-ul atunci când nu s-a putut citi inode-ul de pe disc, el fiind invalid.

Operații cu inode-uri

Obținerea unui inode

Una din principalele operații cu inode-uri este obținerea unui inode (a unei structuri struct inode în VFS). Până la versiunea 2.6.24 a nucleului Linux, dezvoltatorul definea o funcție read_inode. Începând cu versiunea 2.6.25, dezvoltatorul trebuie să definească o funcție <fsname>_iget unde <fsname> este numele sistemului de fișiere. Această funcție este responsabilă cu aflarea inode-ului VFS în cazul în care acesta există sau crearea unui inode nou și completarea acestuia cu informațiile de pe disc.

În general, în cadrul funcției se va apela iget_locked pentru obținerea structurii struct inode din VFS. În cazul în care inode-ul este nou creat, atunci va trebui citit inode-ul de pe disc (folosind sb_bread) și completate informațiile utile.

Un exemplu de astfel de funcție este minix_iget:

static struct inode *V1_minix_iget(struct inode *inode)
{
	struct buffer_head * bh;
	struct minix_inode * raw_inode;
	struct minix_inode_info *minix_inode = minix_i(inode);
	int i;
 
	raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
	if (!raw_inode) {
		iget_failed(inode);
		return ERR_PTR(-EIO);
	...
}
 
struct inode *minix_iget(struct super_block *sb, unsigned long ino)
{
	struct inode *inode;
 
	inode = iget_locked(sb, ino);
	if (!inode)
		return ERR_PTR(-ENOMEM);
	if (!(inode->i_state & I_NEW))
		return inode;
 
	if (INODE_VERSION(inode) == MINIX_V1)
		return V1_minix_iget(inode);
    ...
}

În cadrul funcției minix_iget se obține inode-ul VFS folosind iget_locked. Dacă inode-ul este deja existent (nu este nou = nu este configurat flag-ul I_NEW) funcția se întoarce. Altfel, se apelează funcția V1_minix_iget care va citi inode-ul de pe disc folosind minix_V1_raw_inode și apoi va completa inode-ul VFS cu informațiile citite.

Superoperații

O bună parte din superoperații (componentele structurii struct super_operations, utilizate de superbloc) sunt folosite în lucrul cu inode-uri. În continuare sunt prezentate acestea:

  • alloc_inode: alocă un inode. De obicei, în cadrul acestei structuri se alocă o structură fsname_inode_info și se efectuează inițializările de bază ale inode-ului VFS (folosind inode_init_once). În minix, pentru alocare este folosită funcția kmem_cache_alloc care interacționează cu subsistemul SLAB. La fiecare alocare se apelează constructorul cache-ului, în cazul minix funcția init_once. Alternativ, se poate folosi kmalloc, caz în care va trebui apelată funcția inode_init_once. Funcția alloc_inode va fi apelată de funcțiile new_inode și iget_locked.
  • write_inode: salvează/actualizează pe disc inode-ul primit ca parametru; pentru actualizarea inode-ului, deși ineficient, pentru începători este recomandat să folosească următoarea secvență de operații:
    • încarcă inode-ul de pe disc cu ajutorul funcției sb_bread;
    • modifică buffer-ul în concordanță cu inode-ul de salvat;
    • marchează buffer-ul murdar (dirty) cu ajutorul funcției mark_buffer_dirty; kernel-ul se va ocupa apoi de scrierea lui pe disc;
    • un exemplu este funcția minix_write_inode din sistemul de fișiere minix
  • evict_inode: șterge de pe disc și din memorie orice informație referitoare la numărul inode-ului primit în câmpul i_ino (atât inode-ul de pe disc cât și blocurile de date asociate). Aceasta implică realizarea următoarelor operații:
    • șterge inode-ul de pe disc;
    • actualizează hărțile de biți pe disc (în cazul în care există)
    • șterge inode-ul din page cache prin apelarea funcției truncate_inode_pages;
    • șterge inode-ul din memorie prin apelarea funcției clear_inode ;
    • un exemplu este funcția minix_evict_inode din sistemul de fișiere minix.
  • destroy_inode eliberează memoria ocupată de inode

Operații pentru inode - inode_operations

Operațiile pentru inode sunt descrise de structura struct inode_operations.

Inode-urile sunt de mai multe tipuri: fișier, director, fișier special (pipe, fifo), dispozitiv de tip bloc, dispozitiv de tip caracter, link etc. Din acest motiv, operațiile pe care trebuie un inode să le implementeze sunt diferite pentru fiecare tip de inode. Mai jos vor fi prezentate în detaliu operațiile implementate pentru un inode de tip fișier și un inode de tip director.

Operațiile unui inode sunt inițializate și accesate folosind câmpul i_op al structurii struct inode.

Structura file

Structura file corespunde unui fișier deschis de un proces și există doar în memorie, fiind asociată unui inode. Este entitatea din VFS cea mai apropiată de user-space; câmpurile structurii conțin informații familiare ale unui fișier din user-space (modul de acces, poziția în fișier, etc.), iar operațiile cu aceasta sunt apeluri de sistem cunoscute (read, write, etc.).

Operațiile pentru file sunt descrise de structura struct file_operations.

Pentru a inițializa operațiile pe file pentru un sistem de fișiere, se folosește câmpul i_fop al structurii struct inode. La deschiderea unui fișier, VFS-ul inițializează câmpul f_op al structurii struct file cu adresa din inode→i_fop, astfel încât apeluri de sistem ulterioare să folosească valoarea stocată în file→f_op.

Inode-urile de tip fișier

Pentru lucrul cu inode-ul trebuie completate câmpurile i_op și i_fop ale structurii inode. Tipul inode-ului determină operațiile pe care trebuie să le implementeze.

Operații asupra inode-urilor de tip fișier

În sistemul de fișiere minix, pentru operațiile pe un inode este definită structura minix_file_inode_operations, iar pentru operațiile pe file se definește structura minix_file_operations:

const struct file_operations minix_file_operations = {
         .llseek         = generic_file_llseek,
         .read_iter      = generic_file_read_iter,
         //...
         .write_iter     = generic_file_write_iter,
         //...
         .mmap           = generic_file_mmap,
         //...
};
 
const struct inode_operations minix_file_inode_operations = {
        .setattr        = minix_setattr,
        .getattr        = minix_getattr,
};
 
        //...
        if (S_ISREG(inode->i_mode)) {
                inode->i_op = &minix_file_inode_operations;
                inode->i_fop = &minix_file_operations;
        }
        //...

Funcțiile generic_file_llseek, generic_file_mmap, generic_file_read_iter și generic_file_write_iter sunt implementate în kernel.

Pentru sistemele de fișiere simple nu trebuie să se implementeze decât operația de trunchiere (apelul de sistem truncate). Deși inițial exita o operație dedicată, începând cu 3.14, operația a fost înglobată în setattr: dacă dimensiunea pasată este diferită de dimensiunea curentă a inode-ului atunci va trebui efectuată o operație de trunchiere. Un exemplu de implementare a acestei verificări este prezentă în funcția minix_setattr:

static int minix_setattr(struct dentry *dentry, struct iattr *attr)
{
        struct inode *inode = d_inode(dentry);
        int error;
 
        error = setattr_prepare(dentry, attr);
        if (error)
                return error;
 
        if ((attr->ia_valid & ATTR_SIZE) &&
            attr->ia_size != i_size_read(inode)) {
                error = inode_newsize_ok(inode, attr->ia_size);
                if (error)
                        return error;
 
                truncate_setsize(inode, attr->ia_size);
                minix_truncate(inode);
        }
 
        setattr_copy(inode, attr);
        mark_inode_dirty(inode);
        return 0;
}

Operația de trunchiere presupune:

  • dezalocarea blocurile de date de pe disc care acum sunt în plus (pentru cazul in care noua dimensiune este mai mică) sau alocarea de noi blocuri (pentru cazuri în care noua dimensiune este mai mare)
  • actualizarea hărților de biți pe disc (în cazul în care sunt folosite);
  • actualizarea inode-ului;
  • completarea cu zero spațiul care a rămas nefolosit din ultimul bloc cu ajutorul funcției block_truncate_page.

Un exemplu de implementarea a operației de trunchiere este funcția minix_truncate din sistemul de fișiere minix.

Funcțiile generic_* sunt deja implementate!

Operații asupra spațiului de adresă

Între spațiul de adrese al unui proces și fișiere există o strânsă legătură: execuția programelor se face aproape exclusiv prin maparea fișierului în spațiul de adresă al procesului. Întrucât această abordare funcționează foarte bine și este destul de generală, poate fi folosită și în cazul apelurilor de sistem obișnuite cum ar fi read și write.

Structura care descrie spațiul de adresă este struct address_space, iar operațiile cu aceasta sunt descrise de structura struct address_space_operations. Pentru inițializarea operațiilor asupra spațiului de adresă, se completează câmpul inode->i_mapping->a_ops al inode-ului de tip fișier.

Un exemplu este structura minix_aops din sistemul de fișiere minix:

static const struct address_space_operations minix_aops = {
         .readpage = minix_readpage,
         .writepage = minix_writepage,
         .write_begin = minix_write_begin,
         .write_end = generic_write_end,
         .bmap = minix_bmap
};
 
//...
if (S_ISREG(inode->i_mode)) {
        inode->i_mapping->a_ops = &minix_aops;
}
//...

Funcția generic_write_end este deja implementată. Majoritatea funcțiilor specifice sunt foarte ușor de implementat, după cum urmează:

static int minix_writepage(struct page *page, struct writeback_control *wbc)
{
         return block_write_full_page(page, minix_get_block, wbc);
}
 
static int minix_readpage(struct file *file, struct page *page)
{
         return block_read_full_page(page,minix_get_block);
}
 
static void minix_write_failed(struct address_space *mapping, loff_t to)
{
        struct inode *inode = mapping->host;
 
        if (to > inode->i_size) {
                truncate_pagecache(inode, inode->i_size);
                minix_truncate(inode);
        }
}
 
static int minix_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata)
{
        int ret;
 
        ret = block_write_begin(mapping, pos, len, flags, pagep,
                                minix_get_block);
        if (unlikely(ret))
                minix_write_failed(mapping, pos + len);
 
        return ret;
}
 
static sector_t minix_bmap(struct address_space *mapping, sector_t block)
{
         return generic_block_bmap(mapping,block,minix_get_block);
}

Tot ce mai trebuie făcut este să se implementeze minix_get_block, care trebuie să translateze un bloc al unui fișier într-un bloc de pe device. Dacă flag-ul create primit ca parametru este activat, trebuie alocat un nou bloc. În cazul în care se creează un bloc nou, trebuie marcată corespunzător harta de biți. Pentru a înștiința nucleul să nu mai citească blocul de pe disc, trebuie marcat bh cu set_buffer_new. Trebuie asociat buffer-ul cu blocul cerut prin funcția map_bh.

Structura dentry

Operațiile pe directoare folosesc structura struct dentry. Principala sarcină a acesteia este realizarea de legături între inode-uri și numele fișierelor. Câmpurile importante ale acestei structuri sunt prezentate mai jos:

struct dentry {
        //...
        struct inode             *d_inode;     /* associated inode */
        //...
        struct dentry            *d_parent;    /* dentry object of parent */
        struct qstr              d_name;       /* dentry name */
        //...
 
        struct dentry_operations *d_op;        /* dentry operations table */
        struct super_block       *d_sb;        /* superblock of file */
        void                     *d_fsdata;    /* filesystem-specific data */
        //...
};

Semnificațiile câmpurilor:

  • d_inode: inode-ul referit de acest dentry;
  • d_parent: dentry-ul asociat directorului părinte;
  • d_name: o structură de tip struct qstr ce conține câmpurile name și len cu numele, respectiv lungimea numelui;
  • d_op: operații cu dentry-uri, reprezentate printr-o structură struct dentry_operations. Nucleul implementează operații implicite, astfel încât nu este nevoie să fie (re)implementate. Unele sisteme de fișiere pot face optimizări legate de structura specifică a dentry-urilor;
  • d_fsdata: câmp rezervat sistemului de fișiere ce implementează operații dentry;

Operații cu dentry

Operațiile care se aplică cel mai adesea asupra dentry-urilor sunt:

  • d_make_root: alocă dentry-ul rădăcină. Se folosește în general în funcția care este apelată pentru citirea superblocului (fill_super), care trebuie să inițializeze directorul rădăcină. Așadar se obține inode-ul rădăcină din superbloc și se folosește ca argument pentru această funcție, pentru a completa câmpul s_root din structura struct super_block;
  • d_add: asociază un dentry unui inode; dentry-ul primit ca parametru în apelurile discutate mai sus, semnifică intrarea (nume, lungime nume) ce trebuie să fie creată. Se va folosi această funcție atunci când se creează / încarcă un nou inode care nu are asociat un dentry și care nu a fost introdus încă în hash (la lookup);
  • d_instantiate: versiunea mai light a apelului precedent, în care dentry-ul a fost în prealabil introdus în hash.

Trebuie să se folosească d_instantiate și NU d_add pentru apelurile create, mkdir, mknod, rename, symlink.

Operații asupra inode-urilor de tip director

Operațiile de lucru cu inode-urile de tip director au un nivel de complexitate mai ridicat decât cele de tip fișier. Dezvoltatorul trebuie să definească operații pentru inode-uri și operații pentru file-uri. În minix, aceste operații sunt definite în structurile minix_dir_inode_operations, respectiv minix_dir_operations:

struct inode_operations minix_dir_inode_operations = {
      .create = minix_create,
      .lookup = minix_lookup,
      .link = minix_link,
      .unlink = minix_unlink,
      .symlink = minix_symlink,
      .mkdir = minix_mkdir,
      .rmdir = minix_rmdir,
      .mknod = minix_mknod,
      //...
};
 
struct file_operations minix_dir_operations = {
      .llseek = generic_file_llseek,
      .read = generic_read_dir,
      .iterate = minix_readdir,
      //...
};
 
        //...
	if (S_ISDIR(inode->i_mode)) {
		inode->i_op = &minix_dir_inode_operations;
		inode->i_fop = &minix_dir_operations;
		inode->i_mapping->a_ops = &minix_aops;
	}
       //...

Singura funcție deja implementată este generic_read_dir.

Funcțiile care implementează operațiile asupra inode-urilor de tip director sunt cele descrise mai jos.

Crearea unui inode

Funcția de creare de inode este indicată de câmpul create din structura inode_operations. În cazul minix este vorba de minix_create. Această funcție este apelată în urma apelurilor de sistem creat și open. O astfel de funcție realizează următoarele operații:

  1. Introduce în structura fizică a discului o nouă intrare; nu trebuie uitată actualizarea hărților de biți pe disc.
  2. Configurează drepturile de acces la cele primite ca parametru.
  3. Marchează inode-ul ca murdar (dirty) cu ajutorul funcției mark_inode_dirty.
  4. Instanțiază intrarea de director (dentry) cu ajutorul funcției d_instantiate.

Crearea unui director

Funcția de creare de director este indicată de câmpul mkdir din structura inode_operations. În cazul minix este vorba de minix_mkdir. Această funcție este apelată în urma apelului de sistem mkdir. O astfel de funcție realizează următoarele operații:

  • Apelează minix_create.
  • Alocă un bloc de date pentru director.
  • Introduce intrările ”.” și ”..”.

Funcția de creare de link (hard) este indicată de câmpul link din structura inode_operations. În cazul minix este vorba de minix_link. Această funcție este apelată în urma apelului de sistem link. O astfel de funcție realizează următoarele operații:

  • Leagă dentry-ul nou la inode.
  • Incrementează câmpul i_nlink al inode-ului.
  • Marchează inode-ul ca murdar (dirty) cu ajutorul funcției mark_inode_dirty.

Funcția de creare de link (simbolic) este indicată de câmpul symlink din structura inode_operations. În cazul minix este vorba de minix_symlink. Operațiile care trebuiesc realizate sunt similare cu cele de la minix_link cu deosebirile date de faptul că se creează o legătură simbolică.

Funcția de ștergere a unui link (hard) este indicată de câmpul unlink din structura inode_operations. În cazul minix este vorba de minix_unlink. Această funcție este apelată în urma apelului de sistem unlink. O astfel de funcție realizează următoarele operații:

  1. Șterge din structura fizică a discului intrarea dată ca parametru.
  2. Decrementează contorul i_nlink al inode-ului către care puncta intrarea (altfel inode-ul nu va fi niciodată șters).

Ștergerea unui director

Funcția de ștergere a unui director este indicată de câmpul rmdir din structura inode_operations. În cazul minix este vorba de minix_rmdir. Această funcție este apelată în urma apelului de sistem rmdir. O astfel de funcție realizează următoarele operații:

  1. Realizează operațiile realizate de minix_unlink.
  2. Se asigură că directorul este gol; în caz contrar, se întoarce ENOTEMPTY.
  3. Șterge și blocul/blocurile de date aferente.

Căutarea unui inode într-un director

Funcția de căutare a unei intrări într-un director și de extragere a inode-ului acesteia este indicată de câmpul lookup din structura inode_operations. În cazul minix este vorba de minix_lookup. Această funcție este apelată indirect în momentul în care se doresc informații despre inode-ul aferent unei intrări dintr-un director. O astfel de funcție realizează următoarele operații:

  1. Caută în directorul indicat în dir intrarea cu numele dentry→d_name.name.
  2. În cazul în care intrarea este găsita se va întoarce NULL și se va asocia inode-ul cu numele, cu ajutorul funcției d_add.
  3. În caz contrar, se întoarce ERR_PTR.

Iterarea prin intrările într-un director

Funcția de iterare prin intrările unui director (de listare a conținutului directorului) de link (hard) este indicată de câmpul iterate din structura file_operations. În cazul minix este vorba de minix_readdir. Această funcție este apelată în urma apelului de sistem readdir.

Funcția întoarce fie toate intrările din director, fie doar o parte în momentul în care bufferul alocat pentru aceasta nu este disponibil. Un apel al funcției poate întoarce:

  • un număr egal cu numărul de intrări, dacă este loc în bufferul aferent din user space;
  • un număr mai mic decât numarul de intrări, atât cât a fost loc în bufferul aferent din user space;
  • 0, în cazul în care nu mai sunt intrări de citit.

Funcția va fi apelată consecutiv până în momentul în care se citesc toate întrările disponibile. Funcția este apelată de cel puțin două ori.

  • Este apelată doar de două ori în cazul în care:
    • primul apel citește toate intrările și întoarce numărul acestora;
    • al doilea apel întoarce 0, nemaifiind intrări de citit.
  • Este apelată de mai mult de două ori în cazul în care primul apel nu întoarce numărul total de intrări.

Funcția realizează următoarele operații:

  1. Parcurge intrările (dentry-urile) din directorul curent.
  2. Pentru fiecare dentry citit se incrementează valoarea ctx→pos.
  3. Pentru fiecare dentry valid (un inode diferit de 0, de exemplu), apelează funcția dir_emit.
  4. Dacă funcția dir_emit întoarce o valoare diferită de zero înseamnă că bufferul din user space este umplut și funcția se va întoarce.

Argumentele funcției dir_emit sunt:

  • ctx este contextul de director, transmis ca argument funcției de tipul iterate;
  • name este numele intrării; este un șir de caractere;
  • name_len este lungimea numelui intrării;
  • ino este numărul inode-ului intrării;
  • type identifica tipul intrării: DT_REG (fișier), DT_DIR(director), DT_UNKNOWN etc. Se poate folosi DT_UNKNOWN când nu se cunoaște tipul intrării.

Operații pe bitmap-uri

În cazul lucrului cu sistemul de fișiere, sunt reținute informații de gestiune (ce bloc este liber sau ocupat, ce inode este liber sau ocupat) prin intermediul de hărți de biți (bitmap). Pentru aceasta avem adesea nevoie să folosim operații de lucru pe biți. Astfel de operații sunt:

  • căutarea primului bit 0: reprezentând un bloc sau inode liber
  • marcarea unui bit 1: marcând un bloc sau inode ocupat

Operațiile de lucru cu hărți de biți se găsesc în headerele din include/asm-generic/bitops în special în find.h și atomic.h. Funcții uzuale, cu denumirile indicând rolul lor, sunt:

Aceste funcții primesc de regulă adresa hărții de biți, eventual dimensiunea ei (în biți) și, la nevoie, indexul bitului care se dorește activat (set) sau dezactivat (clear).

Câteva exemple de folosire sunt indicate mai jos:

unsigned int map;
unsigned char array_map[NUM_BYTES];
size_t idx;
int changed;
 
/* Find first zero bit in 32 bit integer. */
idx = find_first_zero_bit(&map, 32);
printk (KERN_ALERT "The %zu-th bit is the first zero bit.\n", idx);
 
/* Find first one bit in NUM_BYTES bytes array. */
idx = find_first_bit(array_map, NUM_BYTES * 8);
printk (KERN_ALERT "The %zu-th bit is the first one bit.\n", idx);
 
/*
 * Clear the idx-th bit in integer.
 * It is assumed idx is less the number of bits in integer.
 */
clear_bit(idx, &map);
 
/*
 * Test and set the idx-th bit in array.
 * It is assumed idx is less the number of bits in array.
 */
changed = __test_and_set_bit(idx, &sbi->imap);
if (changed)
	printk(KERN_ALERT "%zu-th bit changed\n", idx);

Resurse utile

  1. Robert Love – Linux Kernel Development, Second Edition – Chapter 12. The Virtual Filesystem
  2. Understanding the Linux Kernel, 3rd edition - Chapter 12. The Virtual Filesystem
so2/laboratoare/lab09.txt · Last modified: 2021/05/03 11:06 by teodor_stefan.dutu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0