Sysfs jest wirtualnym systemem plików opartym na ramfs. Jego celem jest dostarczenie eleganckiego narzędzia pozwalającego na przegląd, oraz być może zmianę, obiektów w jądrze, ich atrybutów, i powiązań.
/sys/
|-- block
| |-- loop0 -> ../devices/virtual/block/loop0
| |-- loop1 -> ../devices/virtual/block/loop1
| |-- loop2 -> ../devices/virtual/block/loop2
| |-- loop3 -> ../devices/virtual/block/loop3
| |-- loop4 -> ../devices/virtual/block/loop4
| |-- loop5 -> ../devices/virtual/block/loop5
| |-- loop6 -> ../devices/virtual/block/loop6
| |-- loop7 -> ../devices/virtual/block/loop7
| |-- ram0 -> ../devices/virtual/block/ram0
| |-- ram1 -> ../devices/virtual/block/ram1
| |-- ram10 -> ../devices/virtual/block/ram10
| |-- ram11 -> ../devices/virtual/block/ram11
| |-- ram12 -> ../devices/virtual/block/ram12
| |-- ram13 -> ../devices/virtual/block/ram13
| |-- ram14 -> ../devices/virtual/block/ram14
| |-- ram15 -> ../devices/virtual/block/ram15
| |-- ram2 -> ../devices/virtual/block/ram2
| |-- ram3 -> ../devices/virtual/block/ram3
| |-- ram4 -> ../devices/virtual/block/ram4
| |-- ram5 -> ../devices/virtual/block/ram5
| |-- ram6 -> ../devices/virtual/block/ram6
| |-- ram7 -> ../devices/virtual/block/ram7
| |-- ram8 -> ../devices/virtual/block/ram8
| |-- ram9 -> ../devices/virtual/block/ram9
| |-- sda -> ../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
| `-- sr0 -> ../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
|-- bus
|-- class
|-- dev
|-- devices
|-- firmware
|-- fs
|-- kernel
|-- module
`-- power
/sys/block/sda/sda1/
|-- alignment_offset
|-- dev
|-- holders/
|-- partition
|-- power/
|-- size
|-- start
|-- stat
|-- subsystem -> ../../../../../../../../../class/block
|-- trace/
`-- uevent
sudo cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq
1600000
1600000
lub
cat /sys/module/snd_hda_codec/sections/.text
0xf9721000
Sysfs jest bardzo ściśle związany z pojęciem kobiektu. Dlatego najpierw opowiem o kobiektach.
struct kobject {
const char *name; /* nazwa kobiektu */
struct list_head entry; /* węzeł na liście kset -> list */
struct kobject *parent; /* kobiekt będący ojcem danego kobiektu*/
struct kset *kset; /* kset(może być NULL) do którego należy dany kobiekt */
struct kobj_type *ktype; /* ktyp związany z obiektem, który zawiera dany kobiekt */
struct sysfs_dirent *sd; /* wskaźnik na wewnętrzną reprezentację węzłów w sysfs */
struct kref kref; /* służy do zliczania referencji */
...
};
Wskaźnik parent wskazuje na ojca danego kobiektu. To pozwala tworzyć hierarchiczną strukturę obiektów. Kobiekty nie są interesujące same w sobie, dlatego są najczęściej zaszyte w innych strukturach, najczęściej z nazwą kobj, np.
struct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
To pozwala nadawać hierarchiczną strukturę obiektom, które nie są kobiektami.
Jednym z zadań kobiektów jest zliczanie referencji. Odbywa się to przez pole kref, do uzyskiwania dostępu i zwalniania referencji służš tradycyjnie funkcje:
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
Trzeba wyjaśnić typy użyte w definicji struct kobject.
struct kobj_type {
void (*release)(struct kobject *kobj); /* funkcja wywoływana gdy licznik odwołań do kobiektu tego typu osiąga 0 */
struct sysfs_ops *sysfs_ops; /* wskaźnik na strukturę opisujące interfejs do sysfs */
struct attribute **default_attrs; /* Zakończona NULL-em tablica wskaźników na domyślne atrybuty kobiektów danego typu */
};
Struktura kobj_type opisuje typ obiektu, w którym jest zawarty dany kobiekt. Każda struktura, która zawiera kobiekt powinna mieć przypisany ktyp.
struct kset {
struct list_head list; /* głowa listy kobiektów należących do tego kset */
struct kobject kobj; /* przykładowe użycie kobiektu */
...
};
Struktura kset służy do grupowania powiązanych kobiektów. Mogą one być tego samego ktypu lub różnych.
Inicjacja kobiektu odbywa się przez wywołanie(obowiązkowe) funkcji:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
Inicjuje obiekt, ustawiając ktyp, oraz licznik referencji na 1.
By dodać dany kobiekt (wcześniej zainicjowany funkcją kobject_init()) do sysfs trzeba wywołać funkcję:
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
Ustawia ona nazwę oraz rodzica kobiektu. Dodaje także do sysfs katalog odpowiadający danemu kobiektowi, wypełniając go domyślnymi atrybutami, pozyskanymi z jego ktypu. Nowy katalog pojawi się w katalogu rodzica lub katalogu odpowiadającym kset-owi jeśli był ustawiony, a rodzic nie. Funkcja również doda kobiekt na listę jego kset-u, jeśli ten był ustawiony.
Atrybuty są reprezentowane w sysfs jak zwykłe pliku. Dzięki temu można używać takich narzędzi jak echo i cat, by sprawdzać/zmieniać stan obiektów w jądrze.
struct attribute {
char *name; /* nazwa atrybutu */
struct module *owner;
mode_t mode; /* atrybuty dostępu */
};
Struktura sysfs_ops, zawarta w ktypie:
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
Funkcja show dostaje kobiekt, atrybut i bufor wielkości PAGE_SIZE, który ma wypełnić wartością danego atrybutu wskazanego obiektu. Z kolei funkcja store dostaje bufor, w którym jest nowa wartość atrybutu który został przekazany do funkcji.
Jednak patrząc na struct attribute nie widać jakby to miało się dziać. Dlatego najczęściej używane są dodatkowe struktury, opakowujące do atrybutów, np.
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
};
Teraz funkcje show oraz store z struct sysfs_ops będą działały tak: Mając dany kobiekt, znajdą strukturę device, w którym ten kobiekt się znajduje, oraz mając attribute, znajdą device_attribute, w którym się znajduje. Później wywołają już specyficzne funkcje show, store z device_attribute, te funkcje wykonają faktyczną pracę.
Oprócz domyślnych atrybutów jakie znajdują się w strukturze kobj_type, można każdemu kobiektowi dodać jakieś specyficzne atrybuty używając funkcji:
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
W katalogu kobiektu kobj zostanie stworzony plik reprezentujący atrybut attr. Użytkownik musi pamiętać by zapewnić, że funkcje store i show ze struktury sysfs_ops potrafią odpowiednio obsłużyć dodawany argument.
Jak wspomniano sysfs umożliwia przekazywanie informacji o powiązaniach między obiektami. W celu dodania dowolnego powiązania można użyć funkcji:
int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name)
W katalogu obiektu kobj, pojawi się odnośnik do katalogu obiektu target, z nazwą name.
Podstawowa zasada używania sysfs. Każdy kto chce coś dodać do sysfs, musi pamiętać o jednej zasadzie. Atrybuty mają reprezentować jedną wartość. Ogromnie ułatwia to pozyskiwanie danych z sysfs (nie trzeba parsować tych plików), tak jak ma to miejsce w przypadku systemu plików proc. W szczególnych wypadkach możliwe jest, że jeden atrybut reprezentuje tablicę wartości tego samego typu.
Cytat z dokumentacji (sysfs.txt) o użyciu sysfs:
"Mixing types, expressing multiple lines of data, and doing fancy
formatting of data is heavily frowned upon. Doing these things may get
you publically humiliated and your code rewritten without notice."
Dokumentacja w jądrze:
Janina Mincer-Daszkiewicz |