Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share memory between linux program and windows program running through Wine (same computer)?

Tags:

c++

c

linux

ipc

wine

Is there a way (and then how to) share memory between a linux program and a windows program running through wine ?

Since it could be hard to understand why to do such a thing, I give you my situation : I've a proprietary program compiled only for windows, but this program has an open C plugin API. But, I'd like to make part of my code running on a native application (and use other libraries and other advantages of linux), and doing the IPC in a fast way

like image 707
hl037_ Avatar asked Sep 01 '15 11:09

hl037_


1 Answers

The purpose of Wine is to provide a WinAPI-like environment on Unix(-like) systems. This implies that Wine may be considered a separate, API-facaded, "independent" operating system on top and along a Unix-like system. Thus, that machine you say may actually have two OSes, one over the other. Firstly, the "real" (controlling real-hardware) one, that is, GNU/Linux. Secondly, there is the WinAPI implementation known as Wine in top of the POSIX/SUS interfaces.

And, as far as humankind is concerned, there's one, and only one single portable way to create inter-process communication between machines with different operating systems, and, as you may have already noticed, I refer to sockets.

The Wine subsystem may be considered a semi-virtual machine by its own right, isolated from the Linux kernel, but tightly coupled to it at the same time.

For efficiency purposes, my proposal is to use what sockets in conjunction with what I call the SHMNP (Shared Memory Network Protocol) to provide network-wide shared memory. Again, remember, both "machines" (although it's physically just one) shall be though to be independent. The Wine implementation is too dirty for the clumsy details to be easily work-arounded (although that's nothing compared to Cygwin's hacks).

The SHMNP works this way. Note, however, that the SHMNP does not exist! It's just theoretical, and the protocol structures et al are not presented for obvious reasons.

  • Both machines create their own sockets/shared-memory areas (it's assumed they negotiated the area's size previously). At the same time, they choose a port number and one of the machines becomes the server, the other one becoming the client. The connection is initialized.

  • Initially, all "shared" memory in both machines contains uninitialized data (the other machine may have different values for any given shared memory block).

  • Until the connection is closed, if any of the two machines write to any of address of the shared memory area, a message shall be sent to the other machine with the information that changed. The Linux kernel's funky features may be exploited to allow even raw pointers to work perfectly fine with this (see below). I'm, however, not aware of doing it in Windows rather that by specialized ReadNetworkShared() and WriteNetworkShared()-like procedures.

  • The implementation may provide some sort of synchronization mechanism, so to allow network-wide semaphores, mutexes, et al.

Linux kernel specific quirks: Most modern general-purpose hardware architectures and operating systems provide for a way to protect memory from malicious/buggy/unintended use by a user process. Whenever you read/write to memory that isn't mapped in your process's virtual address space, the CPU will notify the operating system kernel that a page fault has occured. Subsequently, the kernel (if Unix(-like)) will send a segmentation violation signal to the offending process, or in other words, you receive SIGSEGV.

The hidden magical secret is that SIGSEGV may be caught, and handled. Thus, we may mmap() some memory (the shared memory area), mark it as read-only with mprotect(), then, whenever we try to write to an address in the shared memory area, the process will receive a SIGSEGV. The signal handler subsequently performs checks in the siginfo_t passed on by the kernel, and deduces one of two actions.

  • If the faulty address is not in the shared memory area, abort() or whatever.
  • Otherwise, the to be written page shall be copied to a temporary storage (maybe with the help of splice()?). Then, mark the to be written page as read/write, and setup a timer so that in within a timeout the page is marked read-only again and the (maybe compressed) difference between the old copy and the now-written page is sent through the socket (SIMD may help you here). The handler then returns, allowing the write (and maybe, other writes!) to complete without further intervention until the timer fires out.

Whenever a machine receives compressed data through the socket, it's simply decompressed and written where it belongs.

Hope this helps you!

Edit: I just found an obvious flaw of the pre-edit design. If a (compressed) page was sent to another machine, that other machine would be unable to differentiate between data that has been modified within the page and data that hasn't been modified. This involves a race condition, where the receiving machine may lose information it hasn't yet sended. However, some more Linux-kernel-specific stuff fixes it.

like image 185
3442 Avatar answered Sep 28 '22 15:09

3442