Linux Kernel Device Driver With C and C++

S

SG

Am 03.11.2012 14:33, schrieb James Kuyper:
[...]
The built-in C types fall into a number of categories, based upon which
operators work on them: arithmetic types, pointer types, array types,
functions. Operator overloads work best when they make something that is
of class type work like a member of one of those categories: extended
numeric types such as quaternions or arrays, smart pointers, containers,
or function objects. They only lead to confusion when used for other
purposes. I don't imagine there's much need for extended arithmetic
types in kernal programming. However, it seems to me that various kinds
of smart pointers or containers could be useful, unless they can't be
properly implemented. If there's any place where kernel code passes
around function pointers, passing around function objects might be a
reasonable alternative.

I agree. Last time I checked, the Linux kernel included things like
(intrusive) reference counting for some kinds of objects. Of course,
explicitly invoking functions for incrementing and decrementing these
counters is error-prone. This could be done with a smart pointer class
in C++. In all fairness I have to mention that I noticed an
optimization used in the kernel code. Whenever a function passes a
pointer to a ref-counted object to another function in the sense that
"ownership is transferred", there is no need to increment the
ref-counter for the new copy of the pointer and decrement it when the
original pointer is destroyed. In C++11 you could get the same
behaviour with a "move-enabled" smart pointer class (a class that
provides a move constructor and a move assignment operator).

With respect to overhead of C++, I'd like to point out a nice report on
the subject:
http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf

Cheers!
SG
 
N

Nobody

I just compiled the above function with both gcc and g++ (version
4.7.0). The generated code was identical apart from a few different
identifiers.

Is there some g++ option that tells it to create a mutex?

A mutex is only required if the object has a constructor which cannot be
determined as being thread-safe. Primitive types don't require a mutex,
and types with a "simple" inline constructor may not require one (but it's
anyone's guess as to how much effort the compiler will go to before it
just adds a mutex to be on the safe side).

C doesn't have constructors, so static variables can be "initialised"
simply by mapping the segment which contains them (data, rodata, bss).

In C++, if a static variable has a constructor, then initialisation
requires code to be executed, and it may matter *when* that code is
executed.

Variables declared at file scope have their constructors executed in some
undefined order before main() is called. Local static variables have their
constructors executed the first time that the function is called ... or at
least the generated code must behave "as if" that is the case.

In some cases, it may not matter if the constructor is executed before
main(), or even at compile time (i.e. the variable may be stored within
the data segment in a fully-initialised state, requiring no code to be
executed at run time).

If it does matter when the constructor is executed, then it must be the
first time that the function is called. For multi-threaded code, this
requires a mutex to deal with the case where the "first time" occurs in
multiple threads simultaneously.
 
S

SG

Am 04.11.2012 04:22, schrieb Nobody:
A mutex is only required if the object has a constructor which cannot be
determined as being thread-safe.

Not true. Dynamic initialization is not restricted to constructors.
You can also call arbitrary functions in a dynamic initializer.
 
J

James Kuyper

Am 03.11.2012 21:22, schrieb Keith Thompson:
Andrew Cooper said:
On 03/11/2012 13:33, James Kuyper wrote: [...]
int save_val(int i)
{
static int saved;
int retval = saved;
saved = i;
return retval;
}

There is a difference between how GCC and G++ treat static local
variables. G++ generates extra code to put a mutex around it to prevent
concurrent initialisation, and this is what requires the runtime
support. (Admittedly this is only G++. Other C++ compilers wont, but
best of luck to you trying to compile even the kernel header files
[...]

I just compiled the above function with both gcc and g++ (version
4.7.0). The generated code was identical apart from a few different
identifiers.

Is there some g++ option that tells it to create a mutex?

It does not surprize me that the code is equal. There is no dynamic
initialization of 'saved' going on.

Andrew Cooper claimed that "Local static variables and global ctors and
dtors require runtime support, ...". He didn't restrict that claim to
dynamically initialized variables. The same is true of his claim that
G++ generates extra code for such variables.
 
S

SG

Am 04.11.2012 14:18, schrieb James Kuyper:
Am 03.11.2012 21:22, schrieb Keith Thompson:
On 03/11/2012 13:33, James Kuyper wrote:
[...]
int save_val(int i)
{
static int saved;
int retval = saved;
saved = i;
return retval;
}

There is a difference between how GCC and G++ treat static local
variables. G++ generates extra code to put a mutex around it to prevent
concurrent initialisation, and this is what requires the runtime
support. (Admittedly this is only G++. Other C++ compilers wont, but
best of luck to you trying to compile even the kernel header files
[...]

I just compiled the above function with both gcc and g++ (version
4.7.0). The generated code was identical apart from a few different
identifiers.

Is there some g++ option that tells it to create a mutex?

It does not surprize me that the code is equal. There is no dynamic
initialization of 'saved' going on.

Andrew Cooper claimed that "Local static variables and global ctors and
dtors require runtime support, ...". He didn't restrict that claim to
dynamically initialized variables. The same is true of his claim that
G++ generates extra code for such variables.

Right. Well, what he should have said is that the mutex only applies to
dynamic initializers for these static function-locals. But such kind of
initialization is not even legal in C as far as I know. So, I don't see
any problem with compiling C headers with a C++ compiler in this regard.

If you want to trigger synchronization, you have to use a dynamic
initializer like this:

const int* create_lut();

const int* get_lut()
{
static const int* lut = create_lut(); // dynamic initialization
}

(It doesn't have to be a object of a class type with a constructor).

If I compile this with G++ for x86-64 I see calls to following functions:
__cxa_guard_acquire
__cxa_guard_release
__cxa_guard_abort
which presumably are part of the "C++ runtime". If I add the switch
"-fno-threadsafe-statics", this goes away, but of course, it's not
longer a C++11 compliant behaviour.

Cheers!
SG
 
K

Kenny McCormack

Sorry clc, but this thread has gone on long enough



No. It isn't.

Indeed. I want to know where are the topicality police when you need them!

Come on, Kiki! Why aren't you posting your usual "You are off topic in CLC"
messages? I'd have expected a half dozen of them, from you alone, by now.

Seriously folks! Here we are discussing C++ (a total no-no), Linux (A super
no-no!), and device drivers (not in userspace, so not topical here). Kiki,
what has happened to you?

--
(This discussion group is about C, ...)

Wrong. It is only OCCASIONALLY a discussion group
about C; mostly, like most "discussion" groups, it is
off-topic Rorsharch [sic] revelations of the childhood
traumas of the participants...
 
K

Kenny McCormack

To be accurate - Linus is hostile towards C++ for the kernel. He
doesn't give a *beep* as to what other people use in user space, just as
long as no one forces /him/ to use it.

Of course, that statement is equivalent to "I have no problem with <*>; I
just don't want them living in my neighborhood."

<*> Insert name of ethnic minority here.
 
E

Edward A. Falk

On 03/11/2012 00:48, Melzzzzz wrote:

To be accurate - Linus is hostile towards C++ for the kernel.

Where to draw the line? Perhaps integrate a python interpreter
into the kernel so people can write drivers in python? How
about javascript? I could think of reasons for adding any of
these, but each one comes at a cost in support, runtime libraries,
and kernel bloat.

True story: I worked in a shop once where there was bourne-shell
code in the kernel. It was a kernel daemon. I forget what it was
used for, but we removed it on the next release.
 
I

Ian Collins

Where to draw the line? Perhaps integrate a python interpreter
into the kernel so people can write drivers in python? How
about javascript? I could think of reasons for adding any of
these, but each one comes at a cost in support, runtime libraries,
and kernel bloat.

Simple, the line is a language that causes none of the above. That
leave two choices, C or C++.
 
J

James Kuyper

Where to draw the line? Perhaps integrate a python interpreter
into the kernel so people can write drivers in python? How
about javascript? I could think of reasons for adding any of
these, but each one comes at a cost in support, runtime libraries,
and kernel bloat.

I think a reasonable place to draw the line is at object file
compatibility - if a compiler can generate an object file compatible
with kernel programming requirements, and with the rest of the files
that make up the kernel, it shouldn't matter which language it compiled
to generate that file (though it certainly helps if it's a language that
allows taking advantage of the information contained in the kernel's C
header files). C++ compilers can generate files compatible with those
produced by C compilers, and (with some exceptions) can read and
correctly interpret C header files, which is why the anti-C++ prejudice
described (and occasionally expressed) in this thread has surprised me.
 
I

Ian Collins

I think a reasonable place to draw the line is at object file
compatibility - if a compiler can generate an object file compatible
with kernel programming requirements, and with the rest of the files
that make up the kernel, it shouldn't matter which language it compiled
to generate that file (though it certainly helps if it's a language that
allows taking advantage of the information contained in the kernel's C
header files). C++ compilers can generate files compatible with those
produced by C compilers, and (with some exceptions) can read and
correctly interpret C header files, which is why the anti-C++ prejudice
described (and occasionally expressed) in this thread has surprised me.

I ceased to be surprised many years ago. The same ignorant prejudice
still continues to be regurgitated year after year.
 
I

Ian Collins

There is one overriding reason for not allowing C++ in the Linux kernel
- consistency. The kernel is big - there are a lot of people working
with it, there is a lot of code maintained by people other than the
original developers, and there are a lot of automated tools that work
with the code. Consistency is key. So it is not just C++ that is not
allowed in the kernel - there is a lot of C that is not allowed either.
To quote from the start of the
<http://www.kernel.org/doc/Documentation/CodingStyle> :

"Tabs are 8 characters, and thus indentations are also 8 characters.
There are heretic movements that try to make indentations 4 (or even 2!)
characters deep, and that is akin to trying to define the value of PI to
be 3."

The Linux kernel is not written in just "plain old C" - it is written in
Linux kernel C, with its own style, conventions, macros, and types.

Project style rules are fine, its' the inaccurate technical arguments
that bug me!
 
N

Nobody

I think a reasonable place to draw the line is at object file
compatibility - if a compiler can generate an object file compatible
with kernel programming requirements,

For an open-source project, that isn't really sufficient.

Using another language would require maintainers to understand that
language (and understand it well; what might be minor details in an
application are a lot more significant in a kernel).

Nothing stops you from writing your own kernel module in C++ (provided
that you can generate object code which works without any support from the
run-time environment), but don't expect it to make it into anyone else's
source tree.
 
D

deech

Hi all,
Sorry for the delay replying. The reason I want to do this is an exercise where I'm attempting to write a device driver in Forth. Unfortunately the closest implementation I could find that compiles down to static library is written in C++. I want to create a device driver that's statically linked tothis library to which it delegates `module_init` and `module_exit`. I justcan't figure out how to build it.
-deech
 
A

Andrew Smallshaw

Where to draw the line? Perhaps integrate a python interpreter
into the kernel so people can write drivers in python? How
about javascript? I could think of reasons for adding any of
these, but each one comes at a cost in support, runtime libraries,
and kernel bloat.

Older (perhaps even current) Sun hardware had built in Forth drivers
for key hardware (framebuffer, disk controller, NIC etc) that were
interpreted at run time as a kind of get-you-home solution for
solving the drivers chicken-and-egg problem, i.e. insert CD containing
drivers for the HBA into the drive connected to that HBA...

It works well and being interpreted it has the advantage of being
(conceptually at least) independent of CPU architecture or operating
mode. I believe most practical implementations simply offloaded
the Forth stuff to the PROM routines but there is no reason you
couldn't host a native implementation.

The key thing isn't the language in and of itself but the contraints
you apply since many facilities are not available. Bear in mind
that with a traditional Unix kernel not even an in-kernel malloc()
is available and this carried on at least as long as SVR3. Even
filesystem access may be problematic and things like accessing the
web or that Postgres server is probably a non-starter.

If you design a language from scratch (as for that Forth subset)
you can avoid the tricky parts that probably don't make sense.
Start with something intended as a hosted implementation from the
outset and start paring back and you'll probably find the result
bears little resemblance to what you started with.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,077
Messages
2,570,567
Members
47,203
Latest member
EmmaSwank1

Latest Threads

Top