Emeric Vigier | 2f62582 | 2012-08-06 11:09:52 -0400 | [diff] [blame] | 1 | DCOP: Desktop COmmunications Protocol |
| 2 | |
| 3 | Preston Brown <pbrown@kde.org> |
| 4 | October 14, 1999 |
| 5 | |
| 6 | Revised and extended by Matthias Ettrich <ettrich@kde.org> |
| 7 | Mar 29, 2000 |
| 8 | |
| 9 | Extended with DCOP Signals by Waldo Bastian <bastian@kde.org> |
| 10 | Feb 19, 2001 |
| 11 | |
| 12 | |
| 13 | Motivation and Background: |
| 14 | -------------------------- |
| 15 | |
| 16 | The motivation behind building a protocol like DCOP is simple. For |
| 17 | the past year, we have been attempting to enable interprocess |
| 18 | communication between KDE applications. KDE already has an extremely |
| 19 | simple IPC mechanism called KWMcom, which is (was!) used for communicating |
| 20 | between the panel and the window manager for instance. It is about as |
| 21 | simple as it gets, passing messages via X Atoms. For this reason it |
| 22 | is limited in the size and complexity of the data that can be passed |
| 23 | (X atoms must be small to remain efficient) and it also makes it so |
| 24 | that X is required. CORBA was thought to be a more effective IPC/RPC |
| 25 | solution. However, after a year of attempting to make heavy use of |
| 26 | CORBA in KDE, we have realized that it is a bit slow and memory |
| 27 | intensive for simple use. It also has no authentication available. |
| 28 | |
| 29 | What we really needed was an extremely simple protocol with basic |
| 30 | authorization, along the lines of MIT-MAGIC-COOKIE, as used by X. It |
| 31 | would not be able to do NEARLY what CORBA was able to do, but for the |
| 32 | simple tasks required it would be sufficient. Some examples of such |
| 33 | tasks might be an application sending a message to the panel saying, |
| 34 | "I have started, stop displaying the 'application starting' wait |
| 35 | state," or having a new application that starts query to see if any |
| 36 | other applications of the same name are running. If they are, simply |
| 37 | call a function on the remote application to create a new window, |
| 38 | rather than starting a new process. |
| 39 | |
| 40 | Implementation: |
| 41 | --------------- |
| 42 | |
| 43 | DCOP is a simple IPC/RPC mechanism built to operate over sockets. |
| 44 | Either unix domain sockets or tcp/ip sockets are supported. DCOP is |
| 45 | built on top of the Inter Client Exchange (ICE) protocol, which comes |
| 46 | standard as a part of X11R6 and later. It also depends on Qt, but |
| 47 | beyond that it does not require any other libraries. Because of this, |
| 48 | it is extremely lightweight, enabling it to be linked into all KDE |
| 49 | applications with low overhead. |
| 50 | |
| 51 | Model: |
| 52 | ------ |
| 53 | |
| 54 | The model is simple. Each application using DCOP is a client. They |
| 55 | communicate to each other through a DCOP server, which functions like |
| 56 | a traffic director, dispatching messages/calls to the proper |
| 57 | destinations. All clients are peers of each other. |
| 58 | |
| 59 | Two types of actions are possible with DCOP: "send and forget" |
| 60 | messages, which do not block, and "calls," which block waiting for |
| 61 | some data to be returned. |
| 62 | |
| 63 | Any data that will be sent is serialized (marshalled, for you CORBA |
| 64 | types) using the built-in QDataStream operators available in all of |
| 65 | the Qt classes. This is fast and easy. In fact it's so little work |
| 66 | that you can easily write the marshalling code by hand. In addition, |
| 67 | there's a simple IDL-like compiler available (dcopidl and dcopidl2cpp) |
| 68 | that generates stubs and skeletons for you. Using the dcopidl compiler |
| 69 | has the additional benefit of type safety. |
| 70 | |
| 71 | This HOWTO describes the manual method first and covers the dcopidl |
| 72 | compiler later. |
| 73 | |
| 74 | Establishing the Connection: |
| 75 | ---------------------------- |
| 76 | |
| 77 | KApplication has gained a method called "KApplication::dcopClient()" |
| 78 | which returns a pointer to a DCOPClient instance. The first time this |
| 79 | method is called, the client class will be created. DCOPClients have |
| 80 | unique identifiers attached to them which are based on what |
| 81 | KApplication::name() returns. In fact, if there is only a single |
| 82 | instance of the program running, the appId will be equal to |
| 83 | KApplication::name(). |
| 84 | |
| 85 | To actually enable DCOP communication to begin, you must use |
| 86 | DCOPClient::attach(). This will attempt to attach to the DCOP server. |
| 87 | If no server is found or there is any other type of error, attach() |
| 88 | will return false. KApplication will catch a dcop signal and display an |
| 89 | appropriate error message box in that case. |
| 90 | |
| 91 | After connecting with the server via DCOPClient::attach(), you need to |
| 92 | register this appId with the server so it knows about you. Otherwise, |
| 93 | you are communicating anonymously. Use the |
| 94 | DCOPClient::registerAs(const QCString &name) to do so. In the simple |
| 95 | case: |
| 96 | |
| 97 | /* |
| 98 | * returns the appId that is actually registered, which _may_ be |
| 99 | * different from what you passed |
| 100 | */ |
| 101 | appId = client->registerAs(kApp->name()); |
| 102 | |
| 103 | If you never retrieve the DCOPClient pointer from KApplication, the |
| 104 | object will not be created and thus there will be no memory overhead. |
| 105 | |
| 106 | You may also detach from the server by calling DCOPClient::detach(). |
| 107 | If you wish to attach again you will need to re-register as well. If |
| 108 | you only wish to change the ID under which you are registered, simply |
| 109 | call DCOPClient::registerAs() with the new name. |
| 110 | |
| 111 | KUniqueApplication automatically registers itself to DCOP. If you |
| 112 | are using KUniqueApplication you should not attach or register |
| 113 | yourself, this is already done. The appId is by definition |
| 114 | equal to kapp->name(). You can retrieve the registered DCOP client |
| 115 | by calling kapp->dcopClient(). |
| 116 | |
| 117 | Sending Data to a Remote Application: |
| 118 | ------------------------------------- |
| 119 | |
| 120 | To actually communicate, you have one of two choices. You may either |
| 121 | call the "send" or the "call" method. Both methods require three |
| 122 | identification parameters: an application identifier, a remote object, |
| 123 | a remote function. Sending is asynchronous (i.e. it returns immediately) |
| 124 | and may or may not result in your own application being sent a message at |
| 125 | some point in the future. Then "send" requires one and "call" requires |
| 126 | two data parameters. |
| 127 | |
| 128 | The remote object must be specified as an object hierarchy. That is, |
| 129 | if the toplevel object is called "fooObject" and has the child |
| 130 | "barObject", you would reference this object as "fooObject/barObject". |
| 131 | Functions must be described by a full function signature. If the |
| 132 | remote function is called "doIt", and it takes an int, it would be |
| 133 | described as "doIt(int)". Please note that the return type is not |
| 134 | specified here, as it is not part of the function signature (or at |
| 135 | least the C++ understanding of a function signature). You will get |
| 136 | the return type of a function back as an extra parameter to |
| 137 | DCOPClient::call(). See the section on call() for more details. |
| 138 | |
| 139 | In order to actually get the data to the remote client, it must be |
| 140 | "serialized" via a QDataStream operating on a QByteArray. This is how |
| 141 | the data parameter is "built". A few examples will make clear how this |
| 142 | works. |
| 143 | |
| 144 | Say you want to call "doIt" as described above, and not block (or wait |
| 145 | for a response). You will not receive the return value of the remotely |
| 146 | called function, but you will not hang while the RPC is processed either. |
| 147 | The return value of send() indicates whether DCOP communication succeeded |
| 148 | or not. |
| 149 | |
| 150 | QByteArray data; |
| 151 | QDataStream arg(data, IO_WriteOnly); |
| 152 | arg << 5; |
| 153 | if (!client->send("someAppId", "fooObject/barObject", "doIt(int)", |
| 154 | data)) |
| 155 | qDebug("there was some error using DCOP."); |
| 156 | |
| 157 | OK, now let's say we wanted to get the data back from the remotely |
| 158 | called function. You have to execute a call() instead of a send(). |
| 159 | The returned value will then be available in the data parameter "reply". |
| 160 | The actual return value of call() is still whether or not DCOP |
| 161 | communication was successful. |
| 162 | |
| 163 | QByteArray data, replyData; |
| 164 | QCString replyType; |
| 165 | QDataStream arg(data, IO_WriteOnly); |
| 166 | arg << 5; |
| 167 | if (!client->call("someAppId", "fooObject/barObject", "doIt(int)", |
| 168 | data, replyType, replyData)) |
| 169 | qDebug("there was some error using DCOP."); |
| 170 | else { |
| 171 | QDataStream reply(replyData, IO_ReadOnly); |
| 172 | if (replyType == "QString") { |
| 173 | QString result; |
| 174 | reply >> result; |
| 175 | print("the result is: %s",result.latin1()); |
| 176 | } else |
| 177 | qDebug("doIt returned an unexpected type of reply!"); |
| 178 | } |
| 179 | |
| 180 | N.B.: You cannot call() a method belonging to an application which has |
| 181 | registered with an unique numeric id appended to its textual name (see |
| 182 | dcopclient.h for more info). In this case, DCOP would not know which |
| 183 | application it should connect with to call the method. This is not an issue |
| 184 | with send(), as you can broadcast to all applications that have registered |
| 185 | with appname-<numeric_id> by using a wildcard (e.g. 'konsole-*'), which |
| 186 | will send your signal to all applications called 'konsole'. |
| 187 | |
| 188 | Receiving Data via DCOP: |
| 189 | ------------------------ |
| 190 | |
| 191 | Currently the only real way to receive data from DCOP is to multiply |
| 192 | inherit from the normal class that you are inheriting (usually some |
| 193 | sort of QWidget subclass or QObject) as well as the DCOPObject class. |
| 194 | DCOPObject provides one very important method: DCOPObject::process(). |
| 195 | This is a pure virtual method that you must implement in order to |
| 196 | process DCOP messages that you receive. It takes a function |
| 197 | signature, QByteArray of parameters, and a reference to a QByteArray |
| 198 | for the reply data that you must fill in. |
| 199 | |
| 200 | Think of DCOPObject::process() as a sort of dispatch agent. In the |
| 201 | future, there will probably be a precompiler for your sources to write |
| 202 | this method for you. However, until that point you need to examine |
| 203 | the incoming function signature and take action accordingly. Here is |
| 204 | an example implementation. |
| 205 | |
| 206 | bool BarObject::process(const QCString &fun, const QByteArray &data, |
| 207 | QCString &replyType, QByteArray &replyData) |
| 208 | { |
| 209 | if (fun == "doIt(int)") { |
| 210 | QDataStream arg(data, IO_ReadOnly); |
| 211 | int i; // parameter |
| 212 | arg >> i; |
| 213 | QString result = self->doIt (i); |
| 214 | QDataStream reply(replyData, IO_WriteOnly); |
| 215 | reply << result; |
| 216 | replyType = "QString"; |
| 217 | return true; |
| 218 | } else { |
| 219 | qDebug("unknown function call to BarObject::process()"); |
| 220 | return false; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | Receiving Calls and processing them: |
| 225 | ------------------------------------ |
| 226 | |
| 227 | If your applications is able to process incoming function calls |
| 228 | right away the above code is all you need. When your application |
| 229 | needs to do more complex tasks you might want to do the processing |
| 230 | out of 'process' function call and send the result back later when |
| 231 | it becomes available. |
| 232 | |
| 233 | For this you can ask your DCOPClient for a transactionId. You can |
| 234 | then return from the 'process' function and when the result is |
| 235 | available finish the transaction. In the mean time your application |
| 236 | can receive incoming DCOP function calls from other clients. |
| 237 | |
| 238 | Such code could like this: |
| 239 | |
| 240 | bool BarObject::process(const QCString &fun, const QByteArray &data, |
| 241 | QCString &, QByteArray &) |
| 242 | { |
| 243 | if (fun == "doIt(int)") { |
| 244 | QDataStream arg(data, IO_ReadOnly); |
| 245 | int i; // parameter |
| 246 | arg >> i; |
| 247 | QString result = self->doIt(i); |
| 248 | |
| 249 | DCOPClientTransaction *myTransaction; |
| 250 | myTransaction = kapp->dcopClient()->beginTransaction(); |
| 251 | |
| 252 | // start processing... |
| 253 | // Calls slotProcessingDone when finished. |
| 254 | startProcessing( myTransaction, i); |
| 255 | |
| 256 | return true; |
| 257 | } else { |
| 258 | qDebug("unknown function call to BarObject::process()"); |
| 259 | return false; |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | slotProcessingDone(DCOPClientTransaction *myTransaction, const QString &result) |
| 264 | { |
| 265 | QCString replyType = "QString"; |
| 266 | QByteArray replyData; |
| 267 | QDataStream reply(replyData, IO_WriteOnly); |
| 268 | reply << result; |
| 269 | kapp->dcopClient()->endTransaction( myTransaction, replyType, replyData ); |
| 270 | } |
| 271 | |
| 272 | DCOP Signals |
| 273 | ------------ |
| 274 | |
| 275 | Sometimes a component wants to send notifications via DCOP to other |
| 276 | components but does not know which components will be interested in these |
| 277 | notifications. One could use a broadcast in such a case but this is a very |
| 278 | crude method. For a more sophisticated method DCOP signals have been invented. |
| 279 | |
| 280 | DCOP signals are very similair to Qt signals, there are some differences |
| 281 | though. A DCOP signal can be connected to a DCOP function. Whenever the DCOP |
| 282 | signal gets emitted, the DCOP functions to which the signal is connected are |
| 283 | being called. DCOP signals are, just like Qt signals, one way. They do not |
| 284 | provide a return value. |
| 285 | |
| 286 | A DCOP signal originates from a DCOP Object/DCOP Client combination (sender). |
| 287 | It can be connected to a function of another DCOP Object/DCOP Client |
| 288 | combination (receiver). |
| 289 | |
| 290 | There are two major differences between connections of Qt signals and |
| 291 | connections of DCOP signals. In DCOP, unlike Qt, a signal connections can |
| 292 | have an anonymous sender and, unlike Qt, a DCOP signal connection can be |
| 293 | non-volatile. |
| 294 | |
| 295 | With DCOP one can connect a signal without specifying the sending DCOP Object |
| 296 | or DCOP Client. In that case signals from any DCOP Object and/or DCOP Client |
| 297 | will be delivered. This allows the specification of certain events without |
| 298 | tying oneself to a certain object that implementes the events. |
| 299 | |
| 300 | Another DCOP feature are so called non-volatile connections. With Qt signal |
| 301 | connections, the connection gets deleted when either sender or receiver of |
| 302 | the signal gets deleted. A volatile DCOP signal connection will behave the |
| 303 | same. However, a non-volatile DCOP signal connection will not get deleted |
| 304 | when the sending object gets deleted. Once a new object gets created with |
| 305 | the same name as the original sending object, the connection will be restored. |
| 306 | There is no difference between the two when the receiving object gets deleted, |
| 307 | in that case the signal connection will always be deleted. |
| 308 | |
| 309 | A receiver can create a non-volatile connection while the sender doesn't (yet) |
| 310 | exist. An anonymous DCOP connection should always be non-volatile. |
| 311 | |
| 312 | The following example shows how KLauncher emits a signal whenever it notices |
| 313 | that an application that was started via KLauncher terminates. |
| 314 | |
| 315 | QByteArray params; |
| 316 | QDataStream stream(params, IO_WriteOnly); |
| 317 | stream << pid; |
| 318 | kapp->dcopClient()->emitDCOPSignal("clientDied(pid_t)", params); |
| 319 | |
| 320 | The task manager of the KDE panel connects to this signal. It uses an |
| 321 | anonymous connection (it doesn't require that the signal is being emitted |
| 322 | by KLauncher) that is non-volatile: |
| 323 | |
| 324 | connectDCOPSignal(0, 0, "clientDied(pid_t)", "clientDied(pid_t)", false); |
| 325 | |
| 326 | It connects the clientDied(pid_t) signal to its own clientDied(pid_t) DCOP |
| 327 | function. In this case the signal and the function to call have the same name. |
| 328 | This isn't needed as long as the arguments of both signal and receiving function |
| 329 | match. The receiving function may ignore one or more of the trailing arguments |
| 330 | of the signal. E.g. it is allowed to connect the clientDied(pid_t) signal to |
| 331 | a clientDied(void) DCOP function. |
| 332 | |
| 333 | Using the dcopidl compiler |
| 334 | --------------------- |
| 335 | |
| 336 | dcopidl makes setting up a DCOP server easy. Instead of having to implement |
| 337 | the process() method and unmarshalling (retrieving from QByteArray) parameters |
| 338 | manually, you can let dcopidl create the necessary code on your behalf. |
| 339 | |
| 340 | This also allows you to describe the interface for your class in a |
| 341 | single, separate header file. |
| 342 | |
| 343 | Writing an IDL file is very similar to writing a normal C++ header. An |
| 344 | exception is the keyword 'ASYNC'. It indicates that a call to this |
| 345 | function shall be processed asynchronously. For the C++ compiler, it |
| 346 | expands to 'void'. |
| 347 | |
| 348 | Example: |
| 349 | |
| 350 | #ifndef MY_INTERFACE_H |
| 351 | #define MY_INTERFACE_H |
| 352 | |
| 353 | #include <dcopobject.h> |
| 354 | |
| 355 | class MyInterface : virtual public DCOPObject |
| 356 | { |
| 357 | K_DCOP |
| 358 | |
| 359 | k_dcop: |
| 360 | |
| 361 | virtual ASYNC myAsynchronousMethod(QString someParameter) = 0; |
| 362 | virtual QRect mySynchronousMethod() = 0; |
| 363 | }; |
| 364 | |
| 365 | #endif |
| 366 | |
| 367 | As you can see, you're essentially declaring an abstract base class, which |
| 368 | virtually inherits from DCOPObject. |
| 369 | |
| 370 | If you're using the standard KDE build scripts, then you can simply |
| 371 | add this file (which you would call MyInterface.h) to your sources |
| 372 | directory. Then you edit your Makefile.am, adding 'MyInterface.skel' |
| 373 | to your SOURCES list and MyInterface.h to include_HEADERS. |
| 374 | |
| 375 | The build scripts will use dcopidl to parse MyInterface.h, converting |
| 376 | it to an XML description in MyInterface.kidl. Next, a file called |
| 377 | MyInterface_skel.cpp will automatically be created, compiled and |
| 378 | linked with your binary. |
| 379 | |
| 380 | The next thing you have to do is to choose which of your classes will |
| 381 | implement the interface described in MyInterface.h. Alter the inheritance |
| 382 | of this class such that it virtually inherits from MyInterface. Then |
| 383 | add declarations to your class interface similar to those on MyInterface.h, |
| 384 | but virtual, not pure virtual. |
| 385 | |
| 386 | Example: |
| 387 | |
| 388 | class MyClass: public QObject, virtual public MyInterface |
| 389 | { |
| 390 | Q_OBJECT |
| 391 | |
| 392 | public: |
| 393 | MyClass(); |
| 394 | ~MyClass(); |
| 395 | |
| 396 | ASYNC myAsynchronousMethod(QString someParameter); |
| 397 | QRect mySynchronousMethod(); |
| 398 | }; |
| 399 | |
| 400 | Note: (Qt issue) Remember that if you are inheriting from QObject, you must |
| 401 | place it first in the list of inherited classes. |
| 402 | |
| 403 | In the implementation of your class' ctor, you must explicitly initialize |
| 404 | those classes from which you are inheriting from. This is, of course, good |
| 405 | practise, but it is essential here as you need to tell DCOPObject the name of |
| 406 | the interface which your are implementing. |
| 407 | |
| 408 | Example: |
| 409 | |
| 410 | MyClass::MyClass() |
| 411 | : QObject(), |
| 412 | DCOPObject("MyInterface") |
| 413 | { |
| 414 | // whatever... |
| 415 | } |
| 416 | |
| 417 | Now you can simply implement the methods you have declared in your interface, |
| 418 | exactly the same as you would normally. |
| 419 | |
| 420 | Example: |
| 421 | |
| 422 | void MyClass::myAsynchronousMethod(QString someParameter) |
| 423 | { |
| 424 | qDebug("myAsyncMethod called with param `" + someParameter + "'"); |
| 425 | } |
| 426 | |
| 427 | |
| 428 | It is not necessary (though very clean) to define an interface as an |
| 429 | abstract class of its own, like we did in the example above. We could |
| 430 | just as well have defined a k_dcop section directly within MyClass: |
| 431 | |
| 432 | class MyClass: public QObject, virtual public DCOPObject |
| 433 | { |
| 434 | Q_OBJECT |
| 435 | K_DCOP |
| 436 | |
| 437 | public: |
| 438 | MyClass(); |
| 439 | ~MyClass(); |
| 440 | |
| 441 | k_dcop: |
| 442 | ASYNC myAsynchronousMethod(QString someParameter); |
| 443 | QRect mySynchronousMethod(); |
| 444 | }; |
| 445 | |
| 446 | In addition to skeletons, dcopidl2cpp also generate stubs. Those make |
| 447 | it easy to call a DCOP interface without doing the marshalling |
| 448 | manually. To use a stub, add MyInterface.stub to the SOURCES list of |
| 449 | your Makefile.am. The stub class will then be called MyInterface_stub. |
| 450 | |
| 451 | Conclusion: |
| 452 | ----------- |
| 453 | |
| 454 | Hopefully this document will get you well on your way into the world |
| 455 | of inter-process communication with KDE! Please direct all comments |
| 456 | and/or suggestions to Preston Brown <pbrown@kde.org> and Matthias |
| 457 | Ettrich <ettrich@kde.org>. |
| 458 | |
| 459 | |
| 460 | Inter-user communication |
| 461 | ------------------------ |
| 462 | |
| 463 | Sometimes it might be interesting to use DCOP between processes |
| 464 | belonging to different users, e.g. a frontend process running |
| 465 | with the user's id, and a backend process running as root. |
| 466 | |
| 467 | To do this, two steps have to be taken: |
| 468 | |
| 469 | a) both processes need to talk to the same DCOP server |
| 470 | b) the authentication must be ensured |
| 471 | |
| 472 | For the first step, you simply pass the server address (as |
| 473 | found in .DCOPserver) to the second process. For the authentication, |
| 474 | you can use the ICEAUTHORITY environment variable to tell the |
| 475 | second process where to find the authentication information. |
| 476 | (Note that this implies that the second process is able to |
| 477 | read the authentication file, so it will probably only work |
| 478 | if the second process runs as root. If it should run as another |
| 479 | user, a similar approach to what kdesu does with xauth must |
| 480 | be taken. In fact, it would be a very good idea to add DCOP |
| 481 | support to kdesu!) |
| 482 | |
| 483 | For example |
| 484 | |
| 485 | ICEAUTHORITY=~user/.ICEauthority kdesu root -c kcmroot -dcopserver `cat ~user/.DCOPserver` |
| 486 | |
| 487 | will, after kdesu got the root password, execute kcmroot as root, talking |
| 488 | to the user's dcop server. |
| 489 | |
| 490 | |
| 491 | NOTE: DCOP communication is not encrypted, so please do not |
| 492 | pass important information around this way. |
| 493 | |
| 494 | |
| 495 | Performance Tests: |
| 496 | ------------------ |
| 497 | A few back-of-the-napkin tests folks: |
| 498 | |
| 499 | Code: |
| 500 | |
| 501 | #include <kapplication.h> |
| 502 | |
| 503 | int main(int argc, char **argv) |
| 504 | { |
| 505 | KApplication *app; |
| 506 | |
| 507 | app = new KApplication(argc, argv, "testit"); |
| 508 | return app->exec(); |
| 509 | } |
| 510 | |
| 511 | Compiled with: |
| 512 | |
| 513 | g++ -O2 -o testit testit.cpp -I$QTDIR/include -L$QTDIR/lib -lkdecore |
| 514 | |
| 515 | on Linux yields the following memory use statistics: |
| 516 | |
| 517 | VmSize: 8076 kB |
| 518 | VmLck: 0 kB |
| 519 | VmRSS: 4532 kB |
| 520 | VmData: 208 kB |
| 521 | VmStk: 20 kB |
| 522 | VmExe: 4 kB |
| 523 | VmLib: 6588 kB |
| 524 | |
| 525 | If I create the KApplication's DCOPClient, and call attach() and |
| 526 | registerAs(), it changes to this: |
| 527 | |
| 528 | VmSize: 8080 kB |
| 529 | VmLck: 0 kB |
| 530 | VmRSS: 4624 kB |
| 531 | VmData: 208 kB |
| 532 | VmStk: 20 kB |
| 533 | VmExe: 4 kB |
| 534 | VmLib: 6588 kB |
| 535 | |
| 536 | Basically it appears that using DCOP causes 100k more memory to be |
| 537 | resident, but no more data or stack. So this will be shared between all |
| 538 | processes, right? 100k to enable DCOP in all apps doesn't seem bad at |
| 539 | all. :) |
| 540 | |
| 541 | OK now for some timings. Just creating a KApplication and then exiting |
| 542 | (i.e. removing the call to KApplication::exec) takes this much time: |
| 543 | |
| 544 | 0.28user 0.02system 0:00.32elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k |
| 545 | 0inputs+0outputs (1084major+62minor)pagefaults 0swaps |
| 546 | |
| 547 | I.e. about 1/3 of a second on my PII-233. Now, if we create our DCOP |
| 548 | object and attach to the server, it takes this long: |
| 549 | |
| 550 | 0.27user 0.03system 0:00.34elapsed 87%CPU (0avgtext+0avgdata 0maxresident)k |
| 551 | 0inputs+0outputs (1107major+65minor)pagefaults 0swaps |
| 552 | |
| 553 | I.e. about 1/3 of a second. Basically DCOPClient creation and attaching |
| 554 | gets lost in the statistical variation ("noise"). I was getting times |
| 555 | between .32 and .48 over several runs for both of the example programs, so |
| 556 | obviously system load is more relevant than the extra two calls to |
| 557 | DCOPClient::attach and DCOPClient::registerAs, as well as the actual |
| 558 | DCOPClient constructor time. |
| 559 | |