Indeed, the professor is very good at both finding and avoiding introducing security flaws. He should surely have taught particular techniques. I don't believe he will fail a student because all the software they picked to check turned out to be very secure. More likely he will fail them or give them a low grade if they do not produce any documentary evidence for the software they checked and the auditing methods they used.
The ten flaws goal is probably "stop looking when you've done that", or "you'll get extra marks for being able to rapidly filter out the better code and concentrating on the crap stuff".
The sound is apalling for me on dialup. I think the server is throttling back its bitrate accordingly so I can't even get a decent stream via mplayer -dumpstream
Does anybody have the streams mirrored in a reasonable quality?
The whole point of the theory is that the difference in energy of 4 Hydrogen nuclei that cannot fuse and 4 Hydrogen nuclei that can is actually fairly low.
The reason it was believed to require massive magnetic containment of superheated plasma was that heating was thought to be the only way to deliver that energy. The resultant plasma tends to expand (lots), but when contained, nuclei have an increased chance of colliding and fusing. The extreme heat is required to make it remotely likely that the nuclei approach each other dead on so that they are not deflected and *all* the kinetic energy is converted into potential-to-fuse energy.
In cold-fusion, the theory is that a catalyst is used to bring the nuclei towards each other under the influence of an electric field. This is done with little chance of deflection due to the geometric configuration of the "vessel" created by the chemical structure of the catalyst. It means that only the energy actually needed for the nuclear reaction is required.
> Never ever put a team member down in front of the others or publically criticize the member.
I just quit a job where I was constantly criticised in private and then the higher bosses were told how great I was in front of me, often within the space of a few minutes. This was extremely discomforting (especially since I was usually criticised for doing precisely what I was asked to do - or for not pointing out a problem when I had previously been glared at for pointing out future problems - even in meetings when asked to raise issues).
To the OP asking for management advice:
If you have criticised your staff, don't make yourself look like a nice manager to your bosses in front of him/her, since it makes you look like a cheating, slimey son-of-a-bitch. Remember that glares and body language are the same as words - or more powerful. You can't hide them, so remember what you felt at the time, and stay consistent with that.
Don't ever pretend that you are being nice to somebody because of the words you used even though they know you hate their guts because of your body language - it is despiriting and depressing. So if you can't be happy with somebody despite an error don't be a manager, because your body language will be clear, then your words will tell them you are a liar.
Though my perception could currently be influenced by the treatment I have recently received from an idiot boss with a string of unkept jobs who barely understood the job he was originally taken on to do, let alone software development.
I disagree, there is certainly an "I" in team, and there will be as long as the team has a human being in it.
The important thing to do as a manager, is to be aware of all the different "I"'s throughout the team and to keep them well fed. Then the "I" doesn't get in the way.
Also, be prepared to leave the project and move on to new ones. Software development always needs new talent and new brooms and that means developing the programmers to set up new projects and help you manage them, and bringing in new talent from your competitors and school.
Don't force a formal approach at all times. The programmers need to play with code, then offer what they've discovered as new sub-projects - you can't plan development without some toy code to start from. Also plan for refactoring and debugging.
Documentation should be finalised *after* the code is finished (which means not quite ever finalised), and specifications should be changed whenever thinko's are encountered during coding, and all users of an altered spec should be notified. users of a spec must not be permitted to work around a bug or thinko - the spec should be changed if it causes problems for other code. Adaptors should be written to support old code that *ought* to remain using the old spec ("ought" is decided on a case by case basis).
You need to identify the tools to automate as much of this as possible because communication is broken by people and politics - get the technology in there doing this stuff for you - that means you need a project admin to run the support systems.
I've read Cerebus the Aardvark up until Church and State 2 (the fourth bound volume) and I have to say, those summaries of the tangents actually manage to sound very much like they were written by Cerebus while he was Prime Minister or Pope.
There is a lot of "extra" work done on a high end GPU that is not put on a northbridge with integrated graphics. The article even states that ATI did some trial and error to work out how much they could get on the northbridge before it became impractical (look at the size of that heatsink).
By putting a framebuffer on the mainboard, they've even reduced the hit due to shared main memory to almost nothing, but some operations normally done on a high end GPU *are* done on the general purpose CPU.
I suspect they will be trying to disprove the theorem that "The US elections are held with methods and technology that reliably produce a result that reflects well the will of the people".
Same reason people swear the moment they hurt themselves. Small children are pissed off.
They can't walk properly, they can't put objects where they want them, they can't stop themselves from pissing or worse. And above all, they keep getting picked up. You'd want to swear under those conditions.
No, I am not a student, I completed my degree over two years ago, and there is no reason for C++ code to be slower than C code except for hurried code and not understanding how to make C++ work for you (how many people bother to write const member functions, etc).
It almost entirely comes down to implementation. The fact that you can translate C almost one-to-one to C++ gives you a place to start with *no* overhead other than name mangled symbols to support overloading (and these can be wrapped for the module API and then stripped). Then add templates and then start using OO. So far very little change other than performace improvements. Then the big change is exceptions. These cause larger binaries normally because the implementation of exceptions usually gives you a short fast path with an extended error path. This is the only size overhead I know of after dealing with the symbols and it is not a lot due to savings in the fast path.
As a test, I just compiled a 250 line C file as C++ after making the necessary adjustments to make it compile. I compiled it as g++ -xc++ -O3 -s -oprogcascc prog.c, and gcc -std=c99 -O3 -s -oprogc prog.c
here are the results: -rwxr-xr-x 1 user group 7200 2004-10-28 23:11 progcascc -rwxr-xr-x 1 user group 7364 2004-10-28 23:12 progc
Now tell me about bloat. Any bloat with C++ code is all just symbols and exception handling or code designed with multiple versions of the same code for different cases to push higher performance. Not to mention that different exception handling code can leave the code size the same, or even *less* depending on the ABI. As I said in another post, g++ is open source and there is no reason why, when faced with being useful for the kernel, that it can't end up providing hooks for non-hosted ABI customisation. Sure it could take a couple of years, but that's no reason not to do it.
the only significant difference between C and C++ that requires different code is that a void* is implicitly converted to any other type of pointer in C, so malloc actually becomes relatively safe.
In C++ new does the same job, ensuring safety all the while (although you *can* still force bad code on the compiler if you really try). So while C++ is not a strict superset of C, it can easily express any code structure from C in an almost identical manner. The natural languages you give are not even very closely related. A more applicable example would be Oxford English versus Yorkshire Downs English. Yorkshire Downs English includes most of Oxford English, but has a few extra constructs (thou, thee, thy, thine to name a few) that allows the story teller to describe the relationships between people much more succinctly and possibly with greater dramatic effect - but can still tell any story as using Oxford English with almost identical effect, effort, and structure.
For story telling, Yorkshire Downs English is better than Oxford English (IMHO)
I have pointed out why I think C++ is better than C in other posts in this thread, so I won't repeat it here.
Your comment on ABI stability in g++ is fair, but I would expect if g++ were used by the kernel, the g++ sources would evolve to provide hooks for standalone operation where the sources being compiled could provide implementation of the ABI.
As to templates not being appropriate for low-level stuff, that is exactly what they are appropriate for. You can use template specialisation to cleanly replace conditional #defines. They check your code better, and can't screw up as much stuff if misused and the compiler can check many types of misuse. They also allow for much better code tailoring for different platforms, which can reduce object code size, and increase performance. And all that is done at compile time, so no problem with low-level issues there.
I agree with you for sure. The reason I think C++ is plain better than C is that you can think through you policy and encode most of it in the classes and templates that the other parts of your code will use. That means there is less thinking required for the same quality - or alternatively do the same thinking and get better quality.
The importance of policy in any large development project cannot be overstated.
Although I now realise I may have completely missed the irony in your post (or was it satire, hm, yes, I think it was satire).
I appreciate your civility (which my other "adversary" in this discussion appeared to have some difficulty with to begin with). A great deal of C++ code *is* just C, that is true. It has similar builtin types with similar semantics. But C++ allows objects to have inline member functions, and constructors can be collapsed to almost eliminate the excess copying problem that I have seen mentioned somewhere in this topic. G++ does very well at this, and clean code with encapsulated logic can be compiled to object code *at least* as good as the C version. There is *still* the possibility of excess copying, but only where code is not inlined - I can imagine extensions that might eliminate this, but there is no point discussing them here as they are not available.
The mechanism by which excessive copying is reduced looks like this (note that I'm not making this the nicest code, as you see A has an excessive member where non-member operator<< should be used - but I wanted the example to just show one thing):
class A { public:
inline A () : data(0) {};
inline A(const A& a) : data(a.data) {};
inline A operator++ () {
++data;
return *this;
}
inline void printit () {
std::cout << data << std::endl;
} private:
int data; };
void fn(const A thea) {
A locala = A(thea);
return ++locala; }
int main () {
A mya = fn(A());
mya.printit(); }
The compiler can (and I think recent g++ *does*) compile main to the equivalent of
int main () {
int myadata;
++myadata;
std::cout << myadata << std::endl; }
which is just as fast as the C version (bugs notwithstanding as I haven't checked this code). Even though there is a copy to the argument of the function, a copy to the local variable in the function, and a copy of the return value. For complex examples, the C++ version can actually be noticeably faster than C.
Macros are not always easily debugged, identifiers can be replaced wholesale by #defining it, but not *before* the define, so the header that defines a static variable parses fine, then you include a header from another part of the kernel, then you use the identifer. In the next version of the kernel somebody #defines the identifier you are using within the second header.
This breaks, and is very hard to debug by code inspection or by kdb. It is resolved by policy ensuring that the namespace for symbols/types/structs doesn't meet with the namespace for preprocessor macro's except for possibly a few agreed core macro's.
And it is similar policy that keeps C++ code safe.
Regarding your comment on the locking order. You make my point very well, in C, you have to manage locking order *at*every*call*point*, in C++ you can manage it where the locks are defined. So you make rules like lock A cannot be taken if lock B is already held. In C you have to make this just policy, in C++ you can do some neat code for these cases )though I'm not going to think it through now) that, depending on situation will BUG at runtime or even flat out refuse to compile. And to the user at the call point, it's just a lock.
As to memory management, I didn't explain memory management in the kernel, so I don't understand how I could have explained it naively. There is not just a small amount of non-paged memory allocated ahead of time. Anytime you need to be able to handle an error, you ensure (before the error can occur) that you have memory in which you can store intermediate data in the process of handling the error - otherwise you are dead in the water. Other than that, memory can be allocated within the kernel via a number of means, including kmalloc and the slab allocator. As to IRQ levels, I have an old first year OS and hardware architecture design textbook that discusses them (by a different name - so I could be misunderstanding what you mean), but that is in non-monolithic designs. I am not familiar with whether allocation is permitted in hard IRQ context in Linux or, if it is, by which of the available mechanisms, but if it is not permitted the hardirq *must* have the memory available to handle the error - whether it is C or C++. Throwing an exception in C++ does not require using any memory: in throw SomeException(blah), sizeof SomeException can be 0, and it can store it's information in the preallocated memory for that instance of the IRQ handler (which can of course be an object). If the C++ implementation doesn't allocate memory that it needs for trundling up the stack ahead of time then that is a Quality of Implementation issue, and different exception handling code is required but C++ is still acceptable.
And my notion of an exception is not naive at all. In well written code, when an exception is caught all objects in the try block are destroyed, and the data they represent goes with them so the destructor *does* whatever else is required for the state and change of state that it stands for. In C you have no choice but to try to remember everything in each error case (or use goto's to the one place - but then that's just like a low-feature exception with no compiler support for enforcing policy). Maintaining an understood state for the system is done by encapsulating things in objects and using destructors to handle keeping everything consistent.
All cleanup due to error occurs in one or both of two places: The destructors and the catch blocks. Mostly in the destructors. And you don't *randomly* clean up everything you can think of in C++, that's what you do in C. In C++ you rigourously clean up everything, whether you could think of it or not, because the compiler knows it's there. In the kernel an unknown error )whether by exception in C++, or error code in C) *must* be understood or passed on up the call stack until either something knows what to do, or the whole operation is abandoned for better or worse. In C, that is a lot of work and checking everything, in C++ it's virtually guaranteed.
Yes, I have debugged at the kernel level, written a boot loader, switched to protected mode inluding operating the GDT, IDT, page tables, vga registers. written a VGA terminal, etc... I am familiar with x86 assembler, C and C++.
When an exception occurs, it must be caught and handled correctly (or propagated if that is appropriate) - just the same as a C error code returned from a function, so no loss there. As an advantage, an exception follows a distinct error path back up the stack frames so an error code is *never* treated like a value - which can spell a horrible death for both your uptime and data.
Within a syscall in process context, an uncaught exception will propagate up and up and up until it reaches a catchall at the syscall entrypoint, which can return an error to user-space without *any* harm to the kernel (provided the code is structured not to leave inconsistent state - but you have precisely the same issue in C, and C++ exceptions actually make it *easy* to clean up the state as you fall back through the stack frames).
Within a hardirq handler, the interrupt vector points to a similar catchall - the issue here is that timer interrupts (until the scheduler becomes tickless) and interrupts notifying of readiness by hardware can be missed, but only if you haven't handed off to a softirq or queued data for processing by a kernel thread. The C symptom of incorrect code is again different - the code carries on with incorrect data and balls' everything up in C, in C++ it gives up and leaves everything in a good state - unless the hardware *requires* servicing, but then your code simply has to be correct whatever language it is.
Within a kernel thread, the entry function will have a catchall that kills the thread and a monitor waiting for it's death can see it, log it and restart. Or you can write the code correctly as you have to with the C version (because if you write incorrect C code, the code carries on with bad data and balls' everything up).
So C++ can actually make the system *more* robust, not less. In particular - the raising of an exception does not kill the system except for rare cases where the calling function *also* doesn't bother to catch errors - and then some bug in a destructor prevents returning the state to how it was ! Which will kill your system in C too (or worse - corrupt data which is *far* less likely with exceptions).
C code has the added policy issue of clearing up resources on error. Since you often have at least two return paths, resource release code *must* be put into each and synchronised. This is precisely what exceptions do best - automatically nonetheless.
That's why you have policy. Seriously, what if the locking order for a set of variables changes in C code? Non-deterministic behaviour.
Any language only goes so far, the thing that determines whether a software development project succeeds or not is policy and attitude.
For example, you can prevent changes in the apparent behaviour of the language by prohibiting operator overloading, template specialisation, and implicit constructors. Oh yeah, banning macros, which you also have the problem with in C.
And you don't have to worry about unexpected exceptions, because even if you didn't try-catch, the compiler cleans up *all* your objects in *every* case and tells the function that called you that you failed, and "here's why". The only reason you need to know about exceptions being thrown is to clean up resources and data-structures - and that is done in the destructors for auto class variables, so no problem there.
Although the idea that C++ compiled object code is bloated is incorrect. It's normally either due to inexperience with the language or due to a poor compiler implementation. Part of the problem is that on old compilers, template instatiations were actually included once for every object file that refered to one. More recent compilers can identify and remove duplicates at link time, or can save templates in a separate file and link it in at the end of the build. (For some templates, though, you can actually be better off with one in each file if each file uses inline member functions, and each uses a different one).
Sure C++ has it's faults, but bloatedness is not one of them - although a standard library may be bloated, but that's not an issue for the kernel.
The kernel developers normally care more about the fast path... use fewer cache lines, and quicker functions with fewer pipeline stalls. Exceptions are ideal for that, as it makes the error case slower than the C version (where the error case is about the same speed as the normal case), but the normal case can be made much faster depending on implementation.
> There's generally a 5-10% performance hit just from having code that might possibly throw an exception, depending on your compiler's implementation.
Indeed, it is *very* dependent on the compiler. since a function that can throw an exception does not indicate error in it's return value, a compiler may not do any check for error (while in C, you must have a conditional branch on most functions... slowwww). C++ can run through assuming everything is fine and no errors can occur, then when an error occurs an exception is thrown, which means the function with the error doesn't follow the return path, but rather jumps elsewhere. The exception code of the implementations runtime, when jumped too like this, gets the details of the exception and the point where it was thrown. it can then look up for that 4KB section of memory (or more, or whatever) for the code that decides what happens next (so each module could have different exception handling routines). Those routines can then run the cleanup routine for the throwing function (ensuring that every resource is freed on every error), find the catch handler for the calling routine and run that - which drops back to just after the try block, so everything can carry on as normal.
The compiled code then is almost as good as without any errors even being possible - and it is much faster than the equivalent C. The only way, in C, to get that sort of performance is with a little help from longjmp and friends - this is not desirable as the language doesn't help you get get it right.
if a == b adds a few kb to the kernel, its because the implementation of operator== for the two types has been inlined. either the compiler did it because it thought it would be good (which you can disable), or it did it because the programmer told it to - which is correct.
the alternative with C is to do compare_atype_btype (&a, &b); and that can suffer from the same problems.
Why ever not? C++ allows for much better code, you just need a compiler that's up to the task, and runtime ABI that is predictable. Granted, standard C++ may not be appropriate, but with some features disallowed, it is ideal (better than C).
All you need is policy that covers use of various features, just like the Linux kernel requires policy on use of C.
multiple platform support becomes template specialisation, locking rules can actually be enforced by the language (ie, to get member functions to access an object, you can require that you call a member function to return a mutex object which has the members, and when that mutex is destroyed naturally, the lock is freed. fast, safe, secure.
The question is how customisable is the compiler for how virtual functions, etc, are implemented. Those are the only issues to be concerned about because C++ is plain better than C.
Haha! I got it and I'm not telling any of you!!!
They're all Klingons for a start
Indeed, the professor is very good at both finding and avoiding introducing security flaws. He should surely have taught particular techniques. I don't believe he will fail a student because all the software they picked to check turned out to be very secure. More likely he will fail them or give them a low grade if they do not produce any documentary evidence for the software they checked and the auditing methods they used.
The ten flaws goal is probably "stop looking when you've done that", or "you'll get extra marks for being able to rapidly filter out the better code and concentrating on the crap stuff".
The sound is apalling for me on dialup. I think the server is throttling back its bitrate accordingly so I can't even get a decent stream via mplayer -dumpstream
Does anybody have the streams mirrored in a reasonable quality?
or 4H -> He + Energy
The whole point of the theory is that the difference in energy of 4 Hydrogen nuclei that cannot fuse and 4 Hydrogen nuclei that can is actually fairly low.
The reason it was believed to require massive magnetic containment of superheated plasma was that heating was thought to be the only way to deliver that energy. The resultant plasma tends to expand (lots), but when contained, nuclei have an increased chance of colliding and fusing. The extreme heat is required to make it remotely likely that the nuclei approach each other dead on so that they are not deflected and *all* the kinetic energy is converted into potential-to-fuse energy.
In cold-fusion, the theory is that a catalyst is used to bring the nuclei towards each other under the influence of an electric field. This is done with little chance of deflection due to the geometric configuration of the "vessel" created by the chemical structure of the catalyst. It means that only the energy actually needed for the nuclear reaction is required.
> Never ever put a team member down in front of the others or publically criticize the member.
I just quit a job where I was constantly criticised in private and then the higher bosses were told how great I was in front of me, often within the space of a few minutes. This was extremely discomforting (especially since I was usually criticised for doing precisely what I was asked to do - or for not pointing out a problem when I had previously been glared at for pointing out future problems - even in meetings when asked to raise issues).
To the OP asking for management advice:
If you have criticised your staff, don't make yourself look like a nice manager to your bosses in front of him/her, since it makes you look like a cheating, slimey son-of-a-bitch.
Remember that glares and body language are the same as words - or more powerful. You can't hide them, so remember what you felt at the time, and stay consistent with that.
Don't ever pretend that you are being nice to somebody because of the words you used even though they know you hate their guts because of your body language - it is despiriting and depressing. So if you can't be happy with somebody despite an error don't be a manager, because your body language will be clear, then your words will tell them you are a liar.
Though my perception could currently be influenced by the treatment I have recently received from an idiot boss with a string of unkept jobs who barely understood the job he was originally taken on to do, let alone software development.
I disagree, there is certainly an "I" in team, and there will be as long as the team has a human being in it.
The important thing to do as a manager, is to be aware of all the different "I"'s throughout the team and to keep them well fed. Then the "I" doesn't get in the way.
Also, be prepared to leave the project and move on to new ones. Software development always needs new talent and new brooms and that means developing the programmers to set up new projects and help you manage them, and bringing in new talent from your competitors and school.
Don't force a formal approach at all times. The programmers need to play with code, then offer what they've discovered as new sub-projects - you can't plan development without some toy code to start from. Also plan for refactoring and debugging.
Documentation should be finalised *after* the code is finished (which means not quite ever finalised), and specifications should be changed whenever thinko's are encountered during coding, and all users of an altered spec should be notified. users of a spec must not be permitted to work around a bug or thinko - the spec should be changed if it causes problems for other code. Adaptors should be written to support old code that *ought* to remain using the old spec ("ought" is decided on a case by case basis).
You need to identify the tools to automate as much of this as possible because communication is broken by people and politics - get the technology in there doing this stuff for you - that means you need a project admin to run the support systems.
I've read Cerebus the Aardvark up until Church and State 2 (the fourth bound volume) and I have to say, those summaries of the tangents actually manage to sound very much like they were written by Cerebus while he was Prime Minister or Pope.
Well done
There is a lot of "extra" work done on a high end GPU that is not put on a northbridge with integrated graphics. The article even states that ATI did some trial and error to work out how much they could get on the northbridge before it became impractical (look at the size of that heatsink).
By putting a framebuffer on the mainboard, they've even reduced the hit due to shared main memory to almost nothing, but some operations normally done on a high end GPU *are* done on the general purpose CPU.
I suspect they will be trying to disprove the theorem that "The US elections are held with methods and technology that reliably produce a result that reflects well the will of the people".
All they need is a counterexample.
Same reason people swear the moment they hurt themselves. Small children are pissed off.
They can't walk properly, they can't put objects where they want them, they can't stop themselves from pissing or worse. And above all, they keep getting picked up. You'd want to swear under those conditions.
Spim, Spim, Spim, Spim, ...
No, I am not a student, I completed my degree over two years ago, and there is no reason for C++ code to be slower than C code except for hurried code and not understanding how to make C++ work for you (how many people bother to write const member functions, etc).
It almost entirely comes down to implementation. The fact that you can translate C almost one-to-one to C++ gives you a place to start with *no* overhead other than name mangled symbols to support overloading (and these can be wrapped for the module API and then stripped). Then add templates and then start using OO. So far very little change other than performace improvements. Then the big change is exceptions. These cause larger binaries normally because the implementation of exceptions usually gives you a short fast path with an extended error path. This is the only size overhead I know of after dealing with the symbols and it is not a lot due to savings in the fast path.
As a test, I just compiled a 250 line C file as C++ after making the necessary adjustments to make it compile. I compiled it as g++ -xc++ -O3 -s -oprogcascc prog.c, and gcc -std=c99 -O3 -s -oprogc prog.c
here are the results:
-rwxr-xr-x 1 user group 7200 2004-10-28 23:11 progcascc
-rwxr-xr-x 1 user group 7364 2004-10-28 23:12 progc
Now tell me about bloat. Any bloat with C++ code is all just symbols and exception handling or code designed with multiple versions of the same code for different cases to push higher performance. Not to mention that different exception handling code can leave the code size the same, or even *less* depending on the ABI. As I said in another post, g++ is open source and there is no reason why, when faced with being useful for the kernel, that it can't end up providing hooks for non-hosted ABI customisation. Sure it could take a couple of years, but that's no reason not to do it.
the only significant difference between C and C++ that requires different code is that a void* is implicitly converted to any other type of pointer in C, so malloc actually becomes relatively safe.
In C++ new does the same job, ensuring safety all the while (although you *can* still force bad code on the compiler if you really try). So while C++ is not a strict superset of C, it can easily express any code structure from C in an almost identical manner. The natural languages you give are not even very closely related. A more applicable example would be Oxford English versus Yorkshire Downs English. Yorkshire Downs English includes most of Oxford English, but has a few extra constructs (thou, thee, thy, thine to name a few) that allows the story teller to describe the relationships between people much more succinctly and possibly with greater dramatic effect - but can still tell any story as using Oxford English with almost identical effect, effort, and structure.
For story telling, Yorkshire Downs English is better than Oxford English (IMHO)
I have pointed out why I think C++ is better than C in other posts in this thread, so I won't repeat it here.
Your comment on ABI stability in g++ is fair, but I would expect if g++ were used by the kernel, the g++ sources would evolve to provide hooks for standalone operation where the sources being compiled could provide implementation of the ABI.
As to templates not being appropriate for low-level stuff, that is exactly what they are appropriate for. You can use template specialisation to cleanly replace conditional #defines. They check your code better, and can't screw up as much stuff if misused and the compiler can check many types of misuse. They also allow for much better code tailoring for different platforms, which can reduce object code size, and increase performance. And all that is done at compile time, so no problem with low-level issues there.
I agree with you for sure. The reason I think C++ is plain better than C is that you can think through you policy and encode most of it in the classes and templates that the other parts of your code will use. That means there is less thinking required for the same quality - or alternatively do the same thinking and get better quality.
The importance of policy in any large development project cannot be overstated.
Although I now realise I may have completely missed the irony in your post (or was it satire, hm, yes, I think it was satire).
I appreciate your civility (which my other "adversary" in this discussion appeared to have some difficulty with to begin with). A great deal of C++ code *is* just C, that is true. It has similar builtin types with similar semantics. But C++ allows objects to have inline member functions, and constructors can be collapsed to almost eliminate the excess copying problem that I have seen mentioned somewhere in this topic. G++ does very well at this, and clean code with encapsulated logic can be compiled to object code *at least* as good as the C version. There is *still* the possibility of excess copying, but only where code is not inlined - I can imagine extensions that might eliminate this, but there is no point discussing them here as they are not available.
The mechanism by which excessive copying is reduced looks like this (note that I'm not making this the nicest code, as you see A has an excessive member where non-member operator<< should be used - but I wanted the example to just show one thing):
class A {
public:
inline A () : data(0) {};
inline A(const A& a) : data(a.data) {};
inline A operator++ () {
++data;
return *this;
}
inline void printit () {
std::cout << data << std::endl;
}
private:
int data;
};
void fn(const A thea)
{
A locala = A(thea);
return ++locala;
}
int main ()
{
A mya = fn(A());
mya.printit();
}
The compiler can (and I think recent g++ *does*) compile main to the equivalent of
int main ()
{
int myadata;
++myadata;
std::cout << myadata << std::endl;
}
which is just as fast as the C version (bugs notwithstanding as I haven't checked this code). Even though there is a copy to the argument of the function, a copy to the local variable in the function, and a copy of the return value. For complex examples, the C++ version can actually be noticeably faster than C.
Macros are not always easily debugged, identifiers can be replaced wholesale by #defining it, but not *before* the define, so the header that defines a static variable parses fine, then you include a header from another part of the kernel, then you use the identifer. In the next version of the kernel somebody #defines the identifier you are using within the second header.
This breaks, and is very hard to debug by code inspection or by kdb. It is resolved by policy ensuring that the namespace for symbols/types/structs doesn't meet with the namespace for preprocessor macro's except for possibly a few agreed core macro's.
And it is similar policy that keeps C++ code safe.
Regarding your comment on the locking order. You make my point very well, in C, you have to manage locking order *at*every*call*point*, in C++ you can manage it where the locks are defined. So you make rules like lock A cannot be taken if lock B is already held. In C you have to make this just policy, in C++ you can do some neat code for these cases )though I'm not going to think it through now) that, depending on situation will BUG at runtime or even flat out refuse to compile. And to the user at the call point, it's just a lock.
As to memory management, I didn't explain memory management in the kernel, so I don't understand how I could have explained it naively. There is not just a small amount of non-paged memory allocated ahead of time. Anytime you need to be able to handle an error, you ensure (before the error can occur) that you have memory in which you can store intermediate data in the process of handling the error - otherwise you are dead in the water. Other than that, memory can be allocated within the kernel via a number of means, including kmalloc and the slab allocator. As to IRQ levels, I have an old first year OS and hardware architecture design textbook that discusses them (by a different name - so I could be misunderstanding what you mean), but that is in non-monolithic designs. I am not familiar with whether allocation is permitted in hard IRQ context in Linux or, if it is, by which of the available mechanisms, but if it is not permitted the hardirq *must* have the memory available to handle the error - whether it is C or C++. Throwing an exception in C++ does not require using any memory: in throw SomeException(blah), sizeof SomeException can be 0, and it can store it's information in the preallocated memory for that instance of the IRQ handler (which can of course be an object). If the C++ implementation doesn't allocate memory that it needs for trundling up the stack ahead of time then that is a Quality of Implementation issue, and different exception handling code is required but C++ is still acceptable.
And my notion of an exception is not naive at all. In well written code, when an exception is caught all objects in the try block are destroyed, and the data they represent goes with them so the destructor *does* whatever else is required for the state and change of state that it stands for. In C you have no choice but to try to remember everything in each error case (or use goto's to the one place - but then that's just like a low-feature exception with no compiler support for enforcing policy). Maintaining an understood state for the system is done by encapsulating things in objects and using destructors to handle keeping everything consistent.
All cleanup due to error occurs in one or both of two places: The destructors and the catch blocks. Mostly in the destructors. And you don't *randomly* clean up everything you can think of in C++, that's what you do in C. In C++ you rigourously clean up everything, whether you could think of it or not, because the compiler knows it's there. In the kernel an unknown error )whether by exception in C++, or error code in C) *must* be understood or passed on up the call stack until either something knows what to do, or the whole operation is abandoned for better or worse. In C, that is a lot of work and checking everything, in C++ it's virtually guaranteed.
Yes, I have debugged at the kernel level, written a boot loader, switched to protected mode inluding operating the GDT, IDT, page tables, vga registers. written a VGA terminal, etc... I am familiar with x86 assembler, C and C++.
When an exception occurs, it must be caught and handled correctly (or propagated if that is appropriate) - just the same as a C error code returned from a function, so no loss there. As an advantage, an exception follows a distinct error path back up the stack frames so an error code is *never* treated like a value - which can spell a horrible death for both your uptime and data.
Within a syscall in process context, an uncaught exception will propagate up and up and up until it reaches a catchall at the syscall entrypoint, which can return an error to user-space without *any* harm to the kernel (provided the code is structured not to leave inconsistent state - but you have precisely the same issue in C, and C++ exceptions actually make it *easy* to clean up the state as you fall back through the stack frames).
Within a hardirq handler, the interrupt vector points to a similar catchall - the issue here is that timer interrupts (until the scheduler becomes tickless) and interrupts notifying of readiness by hardware can be missed, but only if you haven't handed off to a softirq or queued data for processing by a kernel thread. The C symptom of incorrect code is again different - the code carries on with incorrect data and balls' everything up in C, in C++ it gives up and leaves everything in a good state - unless the hardware *requires* servicing, but then your code simply has to be correct whatever language it is.
Within a kernel thread, the entry function will have a catchall that kills the thread and a monitor waiting for it's death can see it, log it and restart. Or you can write the code correctly as you have to with the C version (because if you write incorrect C code, the code carries on with bad data and balls' everything up).
So C++ can actually make the system *more* robust, not less. In particular - the raising of an exception does not kill the system except for rare cases where the calling function *also* doesn't bother to catch errors - and then some bug in a destructor prevents returning the state to how it was ! Which will kill your system in C too (or worse - corrupt data which is *far* less likely with exceptions).
C code has the added policy issue of clearing up resources on error. Since you often have at least two return paths, resource release code *must* be put into each and synchronised. This is precisely what exceptions do best - automatically nonetheless.
That's why you have policy. Seriously, what if the locking order for a set of variables changes in C code? Non-deterministic behaviour.
Any language only goes so far, the thing that determines whether a software development project succeeds or not is policy and attitude.
For example, you can prevent changes in the apparent behaviour of the language by prohibiting operator overloading, template specialisation, and implicit constructors. Oh yeah, banning macros, which you also have the problem with in C.
And you don't have to worry about unexpected exceptions, because even if you didn't try-catch, the compiler cleans up *all* your objects in *every* case and tells the function that called you that you failed, and "here's why". The only reason you need to know about exceptions being thrown is to clean up resources and data-structures - and that is done in the destructors for auto class variables, so no problem there.
I think that deserved +1 Funny.
Although the idea that C++ compiled object code is bloated is incorrect. It's normally either due to inexperience with the language or due to a poor compiler implementation. Part of the problem is that on old compilers, template instatiations were actually included once for every object file that refered to one. More recent compilers can identify and remove duplicates at link time, or can save templates in a separate file and link it in at the end of the build. (For some templates, though, you can actually be better off with one in each file if each file uses inline member functions, and each uses a different one).
Sure C++ has it's faults, but bloatedness is not one of them - although a standard library may be bloated, but that's not an issue for the kernel.
The kernel developers normally care more about the fast path... use fewer cache lines, and quicker functions with fewer pipeline stalls. Exceptions are ideal for that, as it makes the error case slower than the C version (where the error case is about the same speed as the normal case), but the normal case can be made much faster depending on implementation.
> There's generally a 5-10% performance hit just from having code that might possibly throw an exception, depending on your compiler's implementation.
Indeed, it is *very* dependent on the compiler. since a function that can throw an exception does not indicate error in it's return value, a compiler may not do any check for error (while in C, you must have a conditional branch on most functions... slowwww). C++ can run through assuming everything is fine and no errors can occur, then when an error occurs an exception is thrown, which means the function with the error doesn't follow the return path, but rather jumps elsewhere. The exception code of the implementations runtime, when jumped too like this, gets the details of the exception and the point where it was thrown. it can then look up for that 4KB section of memory (or more, or whatever) for the code that decides what happens next (so each module could have different exception handling routines). Those routines can then run the cleanup routine for the throwing function (ensuring that every resource is freed on every error), find the catch handler for the calling routine and run that - which drops back to just after the try block, so everything can carry on as normal.
The compiled code then is almost as good as without any errors even being possible - and it is much faster than the equivalent C. The only way, in C, to get that sort of performance is with a little help from longjmp and friends - this is not desirable as the language doesn't help you get get it right.
if a == b adds a few kb to the kernel, its because the implementation of operator== for the two types has been inlined. either the compiler did it because it thought it would be good (which you can disable), or it did it because the programmer told it to - which is correct.
the alternative with C is to do compare_atype_btype (&a, &b); and that can suffer from the same problems.
Why ever not? C++ allows for much better code, you just need a compiler that's up to the task, and runtime ABI that is predictable. Granted, standard C++ may not be appropriate, but with some features disallowed, it is ideal (better than C).
All you need is policy that covers use of various features, just like the Linux kernel requires policy on use of C.
multiple platform support becomes template specialisation, locking rules can actually be enforced by the language (ie, to get member functions to access an object, you can require that you call a member function to return a mutex object which has the members, and when that mutex is destroyed naturally, the lock is freed. fast, safe, secure.
The question is how customisable is the compiler for how virtual functions, etc, are implemented. Those are the only issues to be concerned about because C++ is plain better than C.