Структурная структура с методом в исходном коде Linux - PullRequest
4 голосов
/ 25 мая 2019

Я читаю код ядра Android и сталкиваюсь с такого рода структурами данных,

static const struct file_operations tracing_fops = {
.open       = tracing_open,
.read       = seq_read,
.write      = tracing_write_stub,
.llseek     = tracing_seek,
.release    = tracing_release,
};

Кто-нибудь может объяснить этот синтаксис вообще?правая часть уравнений - это имена функций, и & tracing_fops позже передается в качестве аргумента другой функции, которая запускает файловую систему debugfs.

Ответы [ 2 ]

2 голосов
/ 25 мая 2019

Назначение является примером использования литералов Compund.Согласно C99 Section # 6.5.2.5 :

Постфиксное выражение, состоящее из имени типа в скобках, за которым следует заключенный в скобки список инициализаторов, представляет собой составной литерал .Он предоставляет неназванный объект, значение которого задается списком инициализаторов.

В более простой версии, согласно Документы GCC: Составные литералы :

Составной литерал выглядит как приведение списка инициализатора агрегата в скобках.Его значение представляет собой объект типа, указанного в приведении, содержащий элементы, указанные в инициализаторе.В отличие от результата приведения, составной литерал является lvalue.ISO C99 и более поздние версии поддерживают составные литералы.В качестве расширения GCC поддерживает составные литералы также в режиме C90 и в C ++, хотя, как объясняется ниже, семантика C ++ несколько отличается.

Простой пример:

struct foo { int x; int y; };

func() {
    struct foo var = { .x = 2, .y = 3 };
    ...
}

В примере вопроса struct file_operations определено в include / linux / fs.h , а tracing_fops находится в kernel / trace / trace.c в дереве исходных текстов Linux.

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
...
};

open, read, write являются указателями на функции , которые являются указателями на функцию.После разыменования указателя функции его можно использовать как обычный вызов функции.Структура tracing_fops имеет тип file_operations.Значения членов указателя функции присваиваются функциям в том же файле trace.c с использованием составных литералов.

С составными литералами нам не нужно явно указывать / присваивать все элементы в типе структуры, потому что другие члены установлены в ноль или ноль.Объекты структуры, созданные с использованием составных литералов, могут быть переданы в функции независимо от порядка членов.Параметры функции должны быть одинаковыми для обеих сторон.Например, параметры

int (*open) (struct inode *, struct file *);

такие же, как

int tracing_open(struct inode *inode, struct file *file);

В объектно-ориентированном программировании эта идея несколько похожа на Таблица виртуальных функций.

2 голосов
/ 25 мая 2019

Это просто инициализация структуры, использующая имена полей для присвоения значений только определенным полям. Вы можете взглянуть на struct initialization в cppreference , который демонстрирует эти варианты использования (и даже более сложные ситуации, такие как пропуск определенных имен полей и т. Д.)

Исходники ядра Linux часто используют структуры, состоящие из наборов указателей функций для связанных операций. Они используются для предоставления различных реализаций одного и того же интерфейса, сродни тому, что было бы достигнуто с помощью наследования классов в объектно-ориентированных языках. Например, в C ++ эта же идея может быть реализована с использованием виртуальных методов, а указатели функций будут храниться в классе vtable (что означает, что это будет неявным, а не явным в C ++.)

Использование этой структуры в C аналогично тому, как вы используете объект класса с использованием виртуальных методов в C ++, поскольку вы можете просто вызвать один из «методов», используя:

int r = fops->open(inode, filp);

Фактический код обычно проверяет, установлен ли член структуры, так как инициализация структуры будет сохранять для указателей, которые явно не упомянуты, значение NULL, что позволяет также использовать этот тип структуры для реализации необязательных операций.

Основное отличие состоит в том, что в C ++ у вас будет неявная ссылка на сам объект (this), в то время как в C вы должны передавать это в качестве дополнительного аргумента в тех случаях, когда это необходимо.

...