6

I am reading android kernel code and I'm facing this kind of data structures ,

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

can someone explain this syntax generally ? right side of equations are functions names and &tracing_fops later is passed as an argument to another function that inits debugfs file system.

2 Answers 2

4

The assignment is an example of using Compund Literals. According to C99 Section #6.5.2.5:

A postfix expression that consists of a parenthesized type name followed by a brace- enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list.

In simpler version, according to GCC docs: Compound literals:

A compound literal looks like a cast of a brace-enclosed aggregate initializer list. Its value is an object of the type specified in the cast, containing the elements specified in the initializer. Unlike the result of a cast, a compound literal is an lvalue. ISO C99 and later support compound literals. As an extension, GCC supports compound literals also in C90 mode and in C++, although as explained below, the C++ semantics are somewhat different.

An simple example:

struct foo { int x; int y; };

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

In the question's example, the struct file_operations is defined in include/linux/fs.h and tracing_fops is in kernel/trace/trace.c file in Linux source tree.

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 *);
...
};

The open, read, write are Function Pointers which are pointers that points to a function. After dereferencing the function pointer, it can be used as normal function call. The tracing_fops structure is file_operations type. The values of function pointer members are assigned to the functions in the same trace.c file using compound literals.

With compound literals, we don't have to explicitly specify/assign all members in the structure type because other members are set to zero or null. Structure objects created using compound literals can be passed to functions without depending on member order. The function parameters should be same for both side. For example, the parameters of

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

is same as

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

In object oriented programming, this idea is somewhat similar as Virtual Function Table.

Sign up to request clarification or add additional context in comments.

2 Comments

it's not true that you have to dereference function pointers in order to use them; you don't have to write (*fop->cb)() instead of just fop->cb() (both are equally valid), just as you don't need to take its address explicitly with & (again both fop->cb = cb and fop->cb = &cb work the same).
this is plain wrong. that what you see is tagged structure initialization , not compound literals. Compound literals is func(.... , &(unsigned long){ 0xfeUL } , ..);
3

This is simply a struct initialization, using field names to assign values to specific fields only. You can take a look at struct initialization at cppreference which demonstrates these use cases (and even more advanced situations, such as omitting specific field names, etc.)

The Linux kernel sources often make use of structs consisting of sets of function pointers for related operations. These are used to provide distinct implementations of the same interface, akin to what would be accomplished using class inheritance in object-oriented languages. For instance, in C++ this same idea would be implemented using virtual methods and the function pointers would be stored in the class vtable (which means this would be implicit rather than explicit in C++.)

Using this struct in C is similar to how you'd use an object of a class using virtual methods in C++, since you can simply call one of the "methods" using:

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

The actual code typically tests whether the struct member is set, since the struct initialization will keep the pointers that are not explicitly mentioned set to NULL, making it possible to use this kind of struct to implement optional operations as well.

The main difference being that in C++ you'd have an implicit reference to the object itself (this), while in C you have to pass that as an additional argument in cases where it's needed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.