I can share some experience with communicating Lua states:
In a program where I have several Lua states within one process, I have a "table" called "shared" - in fact it is "userdata" with __index and __newindex metamethods, but for the Lua programmer it behaves similar to a table. You can store strings, numbers and booleans there. You cannot store threads/coroutines, functions or userdata - they cannot be transferred to another state anyway. Currently it cannot store tables, but it would be possible to extend it to recursively store non-cyclic tables containing only strings, numbers and booleans (maybe I will do this some time). Currently users can serialize such tables themself (e.g. to a JSON string or whatever). All read/write operations are protected by a mutex, and the data is stored in another Lua state that basically only acts as a hashmap here. The code is in production use in a webserver using multiple Lua states in the same process, so it might not fit to your application without adaptations. However, it is MIT licensed, so you can take from there do whatever modification you like: https://github.com/civetweb/civetweb/blob/master/src/mod_lua_shared.inl
Another option to share data is through a database used by several processes. This can be done from Lua using "lsqlite3" ... or any other database binding.
Again you can share string, numbers and booleans. You can use transaction based read/write operations on complete records (tables with proper elements).
It will work with different processes, and also offer persistency: when all your Lua processes are shut down, the data is still in the database.
Depending on the database binding, you may even exchange data between processes running on a different host.
>From my experience, it depends on the background of the users whether or not they will like this. A database is a "strange" element if you are programming pure Lua - you can wrap these operations somehow, but they do not "feel" that natural for a Lua programmer than a table (as in "shared" above).
I also created a module to access shared memories in Lua (Linux only, but it would be easy to add Windows support). A shared memory can be used to exchange data between Lua states in different processes on the same machine - when sharing between processes owned by different users on the same machine, you need to take care to set the user access rights correctly - this might be bothersome but doable. I used shared memory to exchange data between Lua processes and C/C++ processes - between a language with dynamic typing (Lua) and static typing (C). For a shared memory, you need to define a static memory layout - you need to work with address offsets in this shared memory. You also need to take into consideration that C does not have a "string" in the same sense as Lua does - instead it uses a character array with a fixed size, and the string can never grow larger than that.
If you only share between Lua and Lua, you would still need to know where (what memory offset) to put what element - you still have to use a fixed, static memory layout.
You cannot store "whatever you like", but only what has been provided in the shared memory layout - it is not like a "table" where you can add new elements as you like. You cannot do any duck typing with this solution. Using low level shared memory addressing functions directly requires some additional training for a Lua programmer.
I did not use it for "Lua to Lua", but only for "Lua to C", with a static memory layout predefined as a C structure.
So unspectacular, I almost forgot about it: Of course you can use files to share data between Lua states.
Not really a "high performance" solution, but works out of the box without any additional C library.
From my experience, an important criterion is the type system you need to support.
"shared" behaves like a Lua table.
"lsqlite" behaves like a database - you define a table structure and add rows.
"lsh" (and probably any other shared memory solution) behaves like a fixed C data structure.
If you are fine with more or less fixed data structures, you can go with a database or a shared memory.
Variable data structure works better with an approach similar to "shared" - it's currently limited to Lua states in the same process, but that could be adapted to work with multiple processes by combining it with some interprocess communication mechanism. All the "read" and "write" operations in "shared" could be sent through domain sockets (Linux) or a named pipe (Windows) - or any other IPC mechanism, to a process holding all data (the "shared" state). This will keep the "look and feel" of a Lua table without any need to predefine any table structure.