3 ms·
This is a really in depth read about OO design patterns in the Linux Kernel using C: https://lwn.net/Articles/444910/ https://lwn.net/Articles/444910/ The easi
by amw-zero 6y ago
This is a really in depth read about OO design patterns in the Linux Kernel using C: https://lwn.net/Articles/444910/ https://lwn.net/Articles/444910/
The easiest to understand example (for me) is that you can manually implement polymorphism support using function pointers that take in a struct instance as an argument. Polymorphism is fundamentally just a method call that's dependent on which target object it's being called on, so this pattern allows you to pass in the target object (as a struct since it's C) explicitly.
From the article:
> Some simple examples of this in the Linux kernel are the file_lock_operations structure which contains two function pointers each of which take a pointer to a struct file_lock, and the seq_operations vtable which contains four function pointers which each operate on a struct seq_file.
- jeffbee 6y agoThe huge and important difference is when you use this pattern in C the compiler has no idea what you are doing and can't check anything, nor can it reduce the cost of this dispatch mechanism. A compiler for a language where this is a feature can check what you are doing and de-virtualize virtual dispatch. In C you pay the highest possible cost for polymorphism while enjoying the least benefits.
- Jasper_ 6y ago> The huge and important difference is when you use this pattern in C the compiler has no idea what you are doing and can't check anything Function pointers exist in C and are type-checked by the compiler just fine. > A compiler for a language where this is a feature can check what you are doing and de-virtualize virtual dispatch. Virtual dispatch is pretty much only used in the kernel in places where virtualization is necessary. Devirtualization is pretty rare in C++ in practice (I don't believe I've ever seen a "virtual final" method, and my day job is on a large C/C++ codebase).
- jeffbee 6y ago> Function pointers exist in C and are type-checked by the compiler just fine. This amount of type safety is almost useless. Many of the function prototypes, for example in inode and dentry ops, have the exact same signature. If you accidentally swap inode.rmdir with inode.unlink, compiler isn't going to say anything. And it won't be caught in code review either, to the extent that Linux even has code review culture. > I don't believe I've ever seen a "virtual final" method, and my day job is on a large C/C++ codebase It's common in my experience, for performance-sensitive code.
- saagarjha 6y agoYou don't need "virtual final" for devirtualization, the compiler will speculatively devirtualize in some cases.
- temac 6y agoI don't think that kind of optim is usually important in a kernel, especially one architected a lot with dynamical modules. Plus, on the convenience side, you can also use other models (e.g. have some function pointers at instance level) than the one of C++ in ways that do not look like completely different when reading the source. Devirtualization is mainly useful to cope with some C++ self inflicted wounds. Linux obviously never had them the first place.
- amw-zero 6y agoI'm really not a compiler expert, but is this optimization common in C++ for example? I thought all virtual methods incurred the cost of a vtable lookup
- jeffbee 6y agoJust slapping the virtual keyword on some function doesn't do anything. Overrides may or may not happen via vtable. Often compilers can figure out how to dispatch at compile time and don't need the vtable.