Не посоветуете, что почитать о разработке ФС вообще и под линукс в частности? В идеале было бы найти какую-нить статью, где во всех подробностях расписывается код кокой-нить ext2.
Сейчас читаю "Linux: азбука ядра" и "Linux device drivers".
И ещё посоветуйте какой-нить форум, где есть кернелхакеры, можно даже на языке потенциального противника, а то сдаётся мне, что вопросов у меня будет ещё много и они будут не совсем в тему данного сообщества.
Разработка ФС
я вот не понимаю, что вам не почитать-бы код самой ext2? что непонятно - спрашивайте. например у меня. Кстати, к ядру это не имеет отношения. к девайсам тоже, дело в том, что ext2 можно развернуть где угодно - хоть на флешке, хоть на CDROM'е, хоть в любом другом файле.
Термо, плюсануть хотел, но лениво ;)
у меня отключено ибо нех! :D
прежде всего я не понимаю с чего начать разбираться... я плохо представляю какие части вынесены в vfs, как фс регистрируется в системе? что берёт на себя vfs, что драйверы блочных устройств, а что надо реализовывать в самом модуле ФС.
какие части вынесены в vfs
что значит v?
что значит v?
virtual - абстракция, на уровень выше конкретных файловых систем... именно к ней, например, сначала поступают системные вызовы типа read(), write(), open() итп... если я не заблуждаюсь...
Начал потихоньку разбираться... думаю никто не будет против, если буду пользовать эту ветку для записи своих мыслей(а то в кащенко уж больно неудобный форум)... Если что - комментируйте/поправляйте smile
Итак функция инициализации:
fs/ext2/super.c struct file_system_type ext2_fs_type; init_ext2_fs(void)
Показать
static struct file_system_type ext2_fs_type = {
.owner = THIS_MODULE,
.name = "ext2",
.get_sb = ext2_get_sb, // указатель на функцию возвращающую суперблок
.kill_sb = kill_block_super, // соответственно функция убивающая суперблок
.fs_flags = FS_REQUIRES_DEV,
};
static int __init init_ext2_fs(void)
{
int err = init_ext2_xattr(); // инициализация дополнительных атрибутов... пока не интересно
if (err)
return err;
err = init_inodecache(); // а вот тут начинаются проблемы... не понимаю, что за кеш, а самое главное как из этого кеша вытаскиваются иноды (см. дальше)
if (err)
goto out1;
err = register_filesystem(&ext2_fs_type); // регистрация файловой системы
if (err)
goto out;
return 0;
// всякие откаты на случай ошибок
out:
destroy_inodecache();
out1:
exit_ext2_xattr();
return err;
}
сначала посмотрим на ргистрацию ФC
fs/filesystem.c register_filesystem(struct file_system_type * fs)
Показать
/**
* register_filesystem - register a new filesystem
* @fs: the file system structure
*
* Adds the file system passed to the list of file systems the kernel
* is aware of for mount and other syscalls. Returns 0 on success,
* or a negative errno code on an error.
*
* The &struct file_system_type that is passed is linked into the kernel
* structures and must not be freed until the file system has been
* unregistered.
*/
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
BUG_ON(strchr(fs->name, '.'));
if (fs->next)
return -EBUSY;
INIT_LIST_HEAD(&fs->fs_supers); //обнуление списка суперблоков
write_lock(&file_systems_lock);
p = find_filesystem(fs->name, strlen(fs->name)); // ещет по имени, не добавлена ли уже такая fs
if (*p)
res = -EBUSY;
else
*p = fs; // добавление ФС в хвост списка (хитрый финт см. функцию find_filesystem)
write_unlock(&file_systems_lock);
return res;
}
! указатель на начало списка ФС хранится в глобальной переменно *file_systems
include/linux/fs.h struct file_system_type
Показать
struct file_system_type {
const char *name; //имя
int fs_flags;
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
void (*kill_sb) (struct super_block *);
struct module *owner; //модуль, владеющий ФС, видимо для дебага, или если модуль выгружают, чтобы ФС тоже автоматом выгружалась...
struct file_system_type * next; // следующая ФС в списке
struct list_head fs_supers; //заголовок списка суперблоков
//....
// тут ещё какие-то блокировки...
};
// to be continued...
а теперь функция, инициализирующая кеш инодов oO
fs/filesystem.c init_inodecache(); init_once(void *foo)
Показать
// вызывается калбеком откуда-то из кеша...
static void init_once(void *foo)
{
struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
// тип инода ext2 для представления в памяти
rwlock_init(&ei->i_meta_lock);
#ifdef CONFIG_EXT2_FS_XATTR
init_rwsem(&ei->xattr_sem);
#endif
mutex_init(&ei->truncate_mutex);
inode_init_once(&ei->vfs_inode); //забивает инод значениями по умолчанию
}
static int init_inodecache(void)
{
// пока понятия не имею как работают кеши и с чем их едят...
// но тут он, очевидно, создаётся
ext2_inode_cachep = kmem_cache_create("ext2_inode_cache",
sizeof(struct ext2_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD),
init_once);
if (ext2_inode_cachep == NULL)
return -ENOMEM;
return 0;
}
описатель структуры:
fs/ext2/ext2.h struct ext2_inode_info
Показать
/*
* second extended file system inode data in memory
*/
// часть полей и коментариев пропущено
struct ext2_inode_info {
__le32 i_data[15];
__u32 i_flags;
__u32 i_faddr;
__u8 i_frag_no;
__u8 i_frag_size;
__u16 i_state;
__u32 i_file_acl;
__u32 i_dir_acl;
__u32 i_dtime;
__u32 i_block_group;
struct ext2_block_alloc_info *i_block_alloc_info;
__u32 i_dir_start_lookup;
rwlock_t i_meta_lock;
struct mutex truncate_mutex;
struct inode vfs_inode; // инод vfs
struct list_head i_orphan; /* unlinked but open inodes */
};
приведу описатель структуры что б было
include/linux/fs.h struct inode
Показать
struct inode {
struct hlist_node i_hash;
struct list_head i_list; /* backing dev IO list */
struct list_head i_sb_list;
struct list_head i_dentry;
unsigned long i_ino;
atomic_t i_count;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
unsigned int i_blkbits;
u64 i_version;
loff_t i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
blkcnt_t i_blocks;
unsigned short i_bytes;
umode_t i_mode;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem;
const struct inode_operations *i_op;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;
struct file_lock *i_flock;
struct address_space *i_mapping;
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_marks;
#endif
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned int i_flags;
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};
kmem_cache_create полностью приводить не буду, напишу только, что он делает с возвращаемым значением:
mm/slab.c struct kmem_cache *kmem_cache_create ()
Показать
struct kmem_cache *
kmem_cache_create (const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void *))
{
size_t left_over, slab_size, ralign;
struct kmem_cache *cachep = NULL, *pc;
gfp_t gfp;
//....
cachep = kmem_cache_zalloc(&cache_cache, gfp);
if (!cachep)
goto oops;
//....
/*
* Determine if the slab management is 'on' or 'off' slab.
* (bootstrapping cannot cope with offslab caches so don't do
* it too early on. Always use on-slab management when
* SLAB_NOLEAKTRACE to avoid recursive calls into kmemleak)
*/
if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init &&
!(flags & SLAB_NOLEAKTRACE))
/*
* Size is large, assume best to place the slab management obj
* off-slab (should allow better packing of objs).
*/
flags |= CFLGS_OFF_SLAB;
size = ALIGN(size, align);
left_over = calculate_slab_order(cachep, size, align, flags);
if (!cachep->num) {
printk(KERN_ERR
"kmem_cache_create: couldn't create cache %s.\n", name);
kmem_cache_free(&cache_cache, cachep);
cachep = NULL;
goto oops;
}
slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)
+ sizeof(struct slab), align);
/*
* If the slab has been placed off-slab, and we have enough space then
* move it on-slab. This is at the expense of any extra colouring.
*/
if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
flags &= ~CFLGS_OFF_SLAB;
left_over -= slab_size;
}
if (flags & CFLGS_OFF_SLAB) {
/* really off slab. No need for manual alignment */
slab_size =
cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab);
#ifdef CONFIG_PAGE_POISONING
/* If we're going to use the generic kernel_map_pages()
* poisoning, then it's going to smash the contents of
* the redzone and userword anyhow, so switch them off.
*/
if (size % PAGE_SIZE == 0 && flags & SLAB_POISON)
flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER);
#endif
}
cachep->colour_off = cache_line_size();
/* Offset must be a multiple of the alignment. */
if (cachep->colour_off < align)
cachep->colour_off = align;
cachep->colour = left_over / cachep->colour_off;
cachep->slab_size = slab_size;
cachep->flags = flags;
cachep->gfpflags = 0;
if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA))
cachep->gfpflags |= GFP_DMA;
cachep->buffer_size = size;
cachep->reciprocal_buffer_size = reciprocal_value(size);
if (flags & CFLGS_OFF_SLAB) {
cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u);
/*
* This is a possibility for one of the malloc_sizes caches.
* But since we go off slab only for object size greater than
* PAGE_SIZE/8, and malloc_sizes gets created in ascending order,
* this should not happen at all.
* But leave a BUG_ON for some lucky dude.
*/
BUG_ON(ZERO_OR_NULL_PTR(cachep->slabp_cache));
}
cachep->ctor = ctor;
cachep->name = name;
if (setup_cpu_cache(cachep, gfp)) {
__kmem_cache_destroy(cachep);
cachep = NULL;
goto oops;
}
/* cache setup completed, link it into the list */
list_add(&cachep->next, &cache_chain);
oops:
if (!cachep && (flags & SLAB_PANIC))
panic("kmem_cache_create(): failed to create slab `%s'\n",
name);
if (slab_is_available()) {
mutex_unlock(&cache_chain_mutex);
put_online_cpus();
}
return cachep;
}
приведу kmem_cache
mm/slab.c struct kmem_cache
Показать
struct kmem_cache {
unsigned int size, align;
unsigned long flags;
const char *name;
void (*ctor)(void *);
};
если кто знает, может разжевать что такое кеш и с чем его едят?
у кэша во все времена было два значения
а) наличность (наличные деньги)
б) некие промежуточные результаты или данные, которые требуются постоянно в процессе работы
читали это?
http://www.interface.ru/home.asp?artId=8660
вдруг поможет...
ну не читать-же каждый раз иноды с диска? вот они и лежат в памяти - читаются ОДИН раз. причём сразу блоком, ЕМНИП в 4096байт (один инод сейчас в 256 байт). это и называется "кеширование".
кстати, иноды создаются 1 раз при форматировании, их очень много - сотни тысяч, и даже миллионы. Потому читать их ВСЕ невозможно. Приходится читать по мере запросов. В FAT'е читалась вся таблица. В NTFS похоже, но там обычно таблица инодов непрерывная (MFT), и лежит точно в центре диска. В EXT таблица разорвана на куски, и куски разбросаны по всей поверхности. Причём координаты таблиц инодов никогда не меняются, и записаны они в суперблоках (которые тоже равномерно разбросаны по всему разделу. но нужен только один, первый суперблок, остальные для резерва).
приведу описатель структуры что б было
что-то добавили, что-то убавили... это EXT4?
спасибо... понял при чём здесь кеш, оказалось всё достаточно просто и логично... просто одна книжка ввела в заблуждение.
[quote=Fat-Zer]приведу описатель структуры что б было
что-то добавили, что-то убавили... это EXT4?[/quote]
это из include/linux/fs.h, т.е. одна структура для всех ФС... похоже для представления инода в памяти...
это из include/linux/fs.h, т.е. одна структура для всех ФС... похоже для представления инода в памяти...
а... а я смотрел на диске. логично, что в памяти лежат только живые файлы. потому там и нет времени удаления. на диске структура совершенно другая.
Как я понял, сейчас есть только EXT4, EXT2 и EXT3 получаются из четвёрки отключением некоторых фич.
Получается, что при чтении инода он теперь кардинально переделывается (ЕМНИП раньше он просто тупо читался, и структура была точно такой-же).
Я вот что подумал: дело в том, что у ядра имеется СВОЯ, личная память. Мало того, ВСЕ алгоритмы внутри ядра имеют гарантированное потребление памяти. К примеру, внутри ядра не применяется qsort(), причина проста - qsort() имеет худший случай - когда время исполнения и объём памяти равен N*N - если N равно например 1000000, то N*N = 10^12, что приведёт даже для 32х битных систем к потреблению как минимум 10^14 байт. (кстати в венде не так. потому она иногда виснет, а иногда просто падает. Редко, но бывает). Проблема в том, что инодов может потребоваться ОЧЕНЬ много, потому просто так этот кеш в ядро не запихаешь. Однако, несколько лет таки запихали. ИМХО как раз это и привело к трудноуловимому глюку. ИМХО этот "глюк" так и не исправили, просто число файлов увеличилось не так быстро, как память :lol:
А линух как тормозил, так и сейчас тормозит при копировании файлов, если этих файлов много. ИЧСХ, область памяти ядра скрыта даже от рута, и невидима - получается так, что кажется, что памяти хватает, но на самом деле, её недостаточно. Как доказал Кнут ещё в середине прошлого века, любая система будет сильно тормозить при нехватке памяти. Вот мы и имеем - кажется (даже руту), что памяти достаточно, но её не хватает. Ядро судорожно гоняет данные с места на место, при этом загрузка памяти 70%, загрузка CPU 0%, но всё тупит и тормозит. Это принципиально не лечится. Ядро венды ввести в такой режим ещё проще, просто мало кому это нужно. А вот в линуксе мы имеем отличную EXT, которая поддерживает огромное количество файлов сразу, чем усиленно пользуются кодеры...
что это было?
это тоже необходимо знать? :o :o :o
что это было?
это тоже необходимо знать?
если вы собираетесь разрабатывать ФС - да. smile
в линуксе этих фс.. черт ногу сломает.
зачем усложнять себе жись?
Как я понял, сейчас есть только EXT4, EXT2 и EXT3 получаются из четвёрки отключением некоторых фич.
почему? в дереве сырцов отдельно лежат fs/ext2/, fs/ext3/, fs/ext4/ и содержимое в них достаточно сильно(если не полностью) различается.
Получается, что при чтении инода он теперь кардинально переделывается (ЕМНИП раньше он просто тупо читался, и структура была точно такой-же).
это тоже достаточно логично... всё таки поддерживаем не одну ФС...
К примеру, внутри ядра не применяется qsort(), причина проста - qsort() имеет худший случай - когда время исполнения и объём памяти равен N*N - если N равно например 1000000, то N*N = 10^12, что приведёт даже для 32х битных систем к потреблению как минимум 10^14 байт.
это по времени худший случай О(n^2), а памяти он используется не более O(n). да и улучшить можно: если глубина рекурсии достигает опасного n, то вызываем какой-нить пузырёк для подмассивов.
ИМХО как раз это и привело к трудноуловимому глюку.
можно поподробней, а то я этого не застал smile
ИЧСХ, область памяти ядра скрыта даже от рута, и невидима - получается так, что кажется, что памяти хватает, но на самом деле, её недостаточно.
о_О вот эту строчку не переварил... не представляю, как такое может быть(то что скрыта от рута, да и как вообще неучтённая память может быть...) oO
в линуксе этих фс.. черт ногу сломает.
зачем усложнять себе жись?
ни кто за вами бегать с палкой и заставлять использовать что-то конкретное не будет. а зачем - как минимум самообразование.
почему? в дереве сырцов отдельно лежат fs/ext2/, fs/ext3/, fs/ext4/ и содержимое в них достаточно сильно(если не полностью) различается.
как я понял, в ядро вкомпилливается только EXT4. но можно засунуть и лишь EXT2, например для встроенных систем без HDD.
это тоже достаточно логично... всё таки поддерживаем не одну ФС...
логично. я не спорю. просто раньше похоже было по другому. конечно в итоге всё и так приводилось к общему знаменателю, но уже дальше по коду.
это по времени худший случай О(n^2), а памяти он используется не более O(n). да и улучшить можно: если глубина рекурсии достигает опасного n, то вызываем какой-нить пузырёк для подмассивов.
ну O(N) это обычно тоже ОЧЕНЬ много. Учитывая, что рассчитывают таки на log2(N).
да и улучшить можно: если глубина рекурсии достигает опасного n, то вызываем какой-нить пузырёк для подмассивов.
проще не парится, а взять другую сортировку, ЕМНИП так и было сделано. (используется метод Шелла).
можно поподробней, а то я этого не застал
недавно было. совпало по времени со сменой hda на sda (у меня во всяком случае). Там при операциях с файлами всё жутко тупило, мышкой по кнопке нельзя было кликнуть. А загрузка около 0.
многие не застали - этот глюк проявлялся как-то рандомно - типа на дистре XYZ и мамке ABC есть, а на другой мамке/дистре - нету.
вот эту строчку не переварил... не представляю, как такое может быть(то что скрыта от рута, да и как вообще неучтённая память может быть...)
да говорю-же: глюк. Рут спрашивает ядро: "сколько памяти?", ядро отвечает 100. И нагло врёт. :(
в линуксе этих фс.. черт ногу сломает.
зачем усложнять себе жись?
для более эффективного использования инструмента надо знать его свойства. Что-бы было ясно, когда применить шило, когда отвёртку, а когда - лом.