How do you write a daemon in risc os?
Alan Williams (2601) 88 posts |
I am interested in getting a server process to run in the background. Like a web server it needs access to sockets and filesystem without reentrancy issues like filecore in use. The best example of this that I have seen is Atomwide RemoteFS. This doesn’t run on my machine any more but looking inside it there are just relocatable modules. I don’t believe the server process is a wimp module task as it carried on working if you pressed F12 or otherwise blocked the wimp. I suspect it hooks into the call after or call every vectors, but I don’t know how to do that in a rm in c. Does this mean its user mode code? and where & how would C configure the stack under these circumstances? Does anybody know of an open source C example of something that works this way? Alan |
Bryan Hogan (339) 593 posts |
Maybe look at WebJames for inspiration? https://www.riscos.info/index.php/WebJames |
Bryan (8467) 468 posts |
Take a look at |
David J. Ruck (33) 1636 posts |
Trying hard not to remember this too much from industrial control stuff I wrote in the 90s; but use a relocatable module, non blocking socket operations and upcalls for socket activity. Write it in C, the extra points for assembler aren’t worth the additional hair loss. |
Bryan (8467) 468 posts |
or, perhaps, this |
Rick Murray (539) 13850 posts |
The above linked page gives an idea of how to communicate using a singletasking program. For modules, it is a little more complicated. You’ll need to poll using
The trick here is that you need two sorts of callback. The first is, as you say, a CallAfter or CallEvery to check periodically. You need to provide some interlocks so you don’t request a callback when a callback is pending. Usually the callback returns nearly right away. But it isn’t guaranteed. There’s something (*Verify? *CheckMap? I forget…) that blocks the system forever. Sure, it’s a rather contrived case, but if the system is blocked for two minutes, your socket check fires four times a second, and you’ve just piled up a little under 500 callback requests… you get the idea. ;-)
In CMHG, add: generic-veneers: module_socketcallevery module_socketcallback In the module, you will need to define the following: extern void module_socketcallafter(void); extern void module_socketcallback(void); These are created by CMHG and are present in the generated module header. They are the functions that you pass to the OS routines. You then need to write: _kernel_oserror *module_socketcallafter_handler(_kernel_swi_regs *, void *); _kernel_oserror *module_socketcallback_handler(_kernel_swi_regs *, void *); It’s the exact same name with “_handler” suffixed. So what happens is this. The OS calls the CMHG-generated function (the module_socketcallafter one) which messes things around in order to make things ‘safe’ for the C runtime. Like a sort of thunk between the OS and C. This then, in turn, calls your function (the _handler suffixed one) to do the actual work requested. Your function then exits through the CMHG-generated function that undoes the C stuff and puts things back into place to satisfy the OS. Now, how to do this? Well, let’s set this up to use a CallAfter. The simplest way to deal with periodic tickers is to set a one-off CallAfter each time, and just set it up again when required. So, something like this will kick it off: r.r[0] = 25; // come back in 5cs r.r[1] = (int)module_socketcallafter; r.r[2] = (int)wsp; _kernel_swi(OS_CallAfter, &r, &r); ticker_pending = TRUE; If you’re wondering where “wsp” came from, it’s the private word (void *pw) given in your module’s initialisation handler. You should keep a copy of it. Because you can’t do much on a CallAfter (you might even be in IRQ mode but I think it’s SVC with IRQs off), the thing to do here is ask for a CallBack: _kernel_oserror *module_socketcallafter_handler(_kernel_swi_regs *ir, void *pw) { if ( !callback_pending ) { r.r[0] = (int)module_socketcallback; r.r[1] = (int)wsp; _kernel_swi(OS_AddCallBack, &r, &r); callback_pending = TRUE; } ticker_pending = FALSE; return; } And when that fires, it’ll enter the callback handler, which will be like: _kernel_oserror *module_socketcallback_handler(_kernel_swi_regs *ir, void *pw) { // do some socket stuff here // do anything else that needs to be periodic callback_pending = FALSE; r.r[0] = blah blah copy-paste the above CallAfter code here to start the cycle all over again... return; } The reason for the seemingly pointless
Nope. Most module code runs as SVC. Most module code doesn’t need to run as SVC but I didn’t write the thing. ;-)
That’s what the CMHG veneer is for. The stack, register preservation, etc are not your concern. Let CMHG deal with it. I’ll second what D.Ruck says – don’t contemplate assembler. Just don’t. Take a look also at this: https://heyrick.eu/blog/index.php?diary=20150323 |
Alan Williams (2601) 88 posts |
Brilliant! thank you all so much, particularly Rick for such a useful details. The next thing is which tool chain would I choose to use given that I wanted to target multiple other platforms. I am just blowing the dust of NetFSFS which when I last looked at it about 20 years ago was to be my replacement for !awServer except portable and in C. Originaly I wanted to target NT Server, Netware & RISC OS but today Linux and RISC OS will do. I have Norcroft from NutPI on a pi & earlier Norcroft and GCC on an SARPC 4.39 (Currently its building on the RPC and running in a taskwindow) If all the module stuff is as happy under GCC as it is Norcroft what version of GCC would I want to generate pi3/4 friendly objects and is it possible to generate modules that are happy in both the 26 and 32 bit worlds and with the variation of instruction sets from the RPC to Pi4 as well? I feel embarrassed that I need to be asking all these questions, but I seem to have taken my eye off the ball for bit long. |
Chris Mahoney (1684) 2165 posts |
I’m a little confused about the need for the CallAfter stuff above. In my own little server I pass |
Rick Murray (539) 13850 posts |
That would be an alternative, but I never managed to get events working reliably myself. So I gave up and just poll on a CallAfter… |
Jeffrey Lee (213) 6048 posts |
If you want an example of using socket events, vncserver might be worth taking a look at. I’ve wrapped the socket event code in a reusable wrapper so that it can easily be applied to as many sockets as you want (although it will result in lots of EventV claimants). sockevent.h is the main interface – allocate a socket_event_status_t from somewhere non-paged (e.g. RMA), then call sock_event_init(), passing in the event struct, socket handle, and a C function you want to use for the callback. The callback function is called from an actual OS callback (i.e. OS_AddCallBack), so it’s safe to do whatever network or filesystem calls you wish. Then to shut down just call socket_event_stop. To make it work you’ll need sockevent.h & sockevent.c, the socket_eventv and socket_event_callback_vector routines from mcode.s, and the socket_callback/socket_callback_handler irq-handler entry from the CMHG file. Note: the assembler is 26/32bit neutral, but assumes an ARMv3+ CPU. So it’ll need a couple of tweaks if you want ARMv2 support. |
Alan Williams (2601) 88 posts |
I have just had a look at my code and I think that the event approach may be the most efficient because for RISC OS I am actually polling xeconet_read_receive() at the moment. With AUN enabled this should obviate the need to deal with IP sockets directly (or prevent me using all the nice facilities they have depending on how one looks at it) I just enabled AUN on a PI 5.27 and it didn’t immediately connect to my RPC. I will test that with Level4 FS before blaming anything other than my code. |
Bryan (8467) 468 posts |
I am not sure what I would do with it these days, but it would be nice to know that my very expensive Level 4 FS can be used again. |