Alexandre Julliard <[EMAIL PROTECTED]> wrote: > The kernel module approach is interesting, but I'm afraid it's not > really practical to reimplement the complete server in the kernel, > unless we can somehow build both the kernel module and the user-mode > server from the same sources. I have to agree with that... I have implemented mutexes, events, semaphores and some of the file handling in a kernel module, plus CloseHandle and most of WaitForMultipleObjects. I can see also process and thread handling and socket handling working quite well, and I might even be able to manage overlapped I/O. (I have to report, I think, that I rewrote the userspace interface, and I managed to get something like a 20% better turn around time in a tight mutex loop than Win2000 executing the same loop *evil grin*). However, I don't think that, for instance, the registry is particularly suitable to Linux kernel space. If it was stored in specially allocated slab memory caches (making it trivial to dispose of), with all the keys as unicode and linkage blocks forming a tree structure, it would take rather a lot of unswappable memory. As for building from the same sources, I don't think that's practical either, since the set of functions available in the two spaces is different (though you can make system calls from kernel-space, I don't recommend it in this case). > An intermediate approach that may be worth investigating is to keep the > server in user-space but implement a faster request mechanism with a small > kernel module; Unix sockets are not very well suited to the request/reply > model that we need, and I think it should be possible to make the > communication much faster with a specialized kernel call. Basically, I too am thinking of an intermediate approach... (1) Put the bits that call the server all into one place in the source (as is mostly done now). Note that the kernel module I have produced _entirely_ implements the Mutex, Semaphore and Event system calls, so things like CreateMutex and WaitForMultipleObjects would have to go in there. (2) The kernel module takes over handle management, with no userspace intervention required. Reference counting, handle number allocation, etc. all done in kernel space. (3) Provide a sort of RPC mechanism for those services that are impractical to implement in kernel space (such as the registry). I've outlined such a mechanism in an attachment to this message. And some miscellaneous optional extra bits: (1) We could use a spare system call to provide access to the kernel module, rather than using ioctl as I do now. This will bypass the overhead of ioctl looking for standard, internal commands to deal with first. (2) Provide calls to map UNIX fd's into file handles, and vice versa. (3) Make CreateFileHandle walk the directory tree itself (rather than relying on filp_open in the kernel), thus allowing much faster support for case-independent matching. And for cases where a kernel module can't be used (such as a non-Linux platform) switch to a different directory in the source tree, that uses the current UNIX socket based approach. David Howells
Here's an idea for RPC mechanism. I think this should be fairly easy to implement, given the basic kernel module I've already got. This is, I think, very similar to the Solaris door mechanism. SERVER IMPLEMENTATION ===================== (a) Use the handle management capability of the kernel module to look after keeping track of requests and replies in the server. (b) Have the server create a Service object, which it can then wait on and accept requests through (similar to server sockets). Multiple threads will be able to wait in this way. hServer = CreateWineService("registry-access"); WaitForMultipleObjects(1,&hServer,0,INFINITE); /* optional */ void *params; hRequest = AcceptWineServiceRequest(hServer,¶ms); (c) Either: (1) allow the server process to access the memory space of the client via a direct ptrace-type mechanism. This should be very fast, and require minimal copying. RegistryRequest rr; ReadWineServiceData(hRequest,params,rr,sizeof(rr)); WriteWineServiceData(hRequest,...); (2) or actually have the service request acceptor take a buffer into which the request is copied, and then have the request reply function take a buffer which holds the reply. (d) Finally, have a reply/result function that closes the handle and finishes the request: ReplyWineServiceRequest(hRequest,reqerrno,result); CLIENT IMPLEMENTATION ===================== (a) As far as the client is concerned, some requests will be handled internally inside the kernel module, for instance, RegOpenKey. The kernel module may make a handle for use by the client, keeping some context information in the kernel object attached to the handle. (b) It will also be possible for arbitrary services to be provided: result = InvokeWineService("myservice",param); or: hRequest = InvokeWineServiceAsync(param); WaitForMultipleObjects(1,&hRequest,0,INFINITE); result = DoneWineService(hRequest); Note that in the asynchronous case, the client must be careful as the service may be read or write the request/reply buffers until the request is complete. (c) If a client is interrupted or killed during a service request, or if it closes the request handle obtained from an asynchronous call, the next time the server tries to access the handle it will be given a suitable error to say the client is no longer available. In addition, the server's request handle will become signalled (and so will release a wait done upon it). (d) If a client is interrupted and the call restarted, the invocation will be reissued by the kernel.