Archived
3
0
This repository has been archived on 2024-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
libipc-old/docs/libipc.md

330 lines
7.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Before starting
This file is a presentation.
Better get the point tools here: https://git.baguette.netlib.re/Baguette/pointtools
TODO: Explain the problem
TODO: Explain the solution
TODO: Explain why LibIPC
TODO: Explain how LibIPC
TODO: Explain other possible implementations
TODO: Explain future of LibIPC
TODO: Explain what can be done right now
TODO: Explain what I actually do with it
TODO: Explain LibIPC isn't a silver bullet but fine for what I want
Have fun!
## Programming problems
Libraries
* change often = hard to follow
* don't often provide a high-level interface
* each library is coded in its own way
* availability vary depending on the language
Example: libraries to access databases
* languages often have their own implementation
* functions may vary from a language to another
## Infrastructure problems
Infrastructure
* Always need to install all required libraries
* No clear way to sandbox part of an application
## What solution?
MAKE APPLICATIONS, NOT LIBRARIES
* apps talking to apps
Create an abstraction for
libraries
* languages, implementations, code modifications in general
network code
* networking: performed by dedicated services
- example: TCPd, UDPd, TLSd, HTTPd...
- LibIPC is independant from protocols and formats
* applications think communications are local
## In practice
usage must be simple
1. init connection or service
2. loop over events
#pause
events are simple and high level
1. connection and disconnection
2. message received and sent
#pause
message have a simple format: length + value
#pause
And that's it.
## When
LibIPC is useful when the app:
- cannot be a simple shell command
- needs a bidirectional communication
- is an abstraction over a library
## Available libraries
* DBUS
* libevent
* even more complicated stuff
* RPC-style, like Corba
#pause
* ... or bare libc api
* shared memory
* pipes
* sockets (unix, inet, inet6)
## DBUS
* not well suited for our needs
(a polite way to say: what a bloody mess)
Is it designed *NOT* to be used?
* over-engineered
* complex
* documentation isn't great
* no code example
## DBUS (bonus page!)
DBUS feels obsolete: a big chunk of the documentation is
about message format. Just use CBOR already!
#pause
They even admit they did a poor job on the C part:
> There is a low-level C binding, but that is probably too detailed
> and cumbersome for anything but writing other bindings.
#pause
Oh. And C++. YOU SHALL NOT PASS!
This is a Linux requirement nowadays, wth?
## libevent
* works with epoll and kqueue
* great performances
* works on Linux and *BSD
* a bit complicated
## Bare libc api
shared memory and semaphores
* (kinda) complicated api
* not about exchanging messages
pipes, sockets
* lack a conventional message format
... but that's about it
* Great to start with!
All have great performances to exchange data.
What is slow is the function to _wait_ for new events.
## LibIPC's choice
Unix sockets
- fast, simple, reliable, bidirectional
- remote connections will have their own service (ex: TCPd)
Dumbest possible message format
- length + value
- build your own format on top of it!
Wait on file descriptors with poll(2)
- slow, but available everywhere
- may upgrade to libevent
## LibIPC history (1/2)
1. based on pipes
* because we gotta go fast!
* ... but implementation was a bit of a mess
#pause
2. rewrite to work with unix sockets
* performances are excellent, no need for _absolute best_
* way nicer implementation
* select(2) for listening on file descriptors
#pause
* ... wait, does select(2) support more than 1024 connections?
## LibIPC history (2/2)
3. rewrite using poll(2)
* many bugfixes later, way more tested than before
* implementation was (kinda) production-ready
* implementation was simple: < 2000 lines of C code
Still wasn't as simple as I wanted
## Current implementation of libIPC
Written in Zig
bindings available in Crystal
* as well as fancy mappings: JSON and CBOR class serialization
#pause
epoll (Linux) and kqueue (*BSD) were avoided
* 'cause callbacks hell => harder to read and to write code
#pause
* but we need them for better performances with many connections
though, API should stay the same for non threaded applications (simple implementation)
#pause
LibIPC doesn't handle parallelism, yet
## How libIPC works
LibIPC has a high level API for the user
1. init a connection (client) or create an unix socket (service)
example: ipc_service_init (context, "service")
#pause
2. loop, wait for events
listening to file descriptors (libIPC ones or not)
example:
while(1) {
wait_event (context, &event, &timer)
switch (event.type) {
case IPC_CONNECTION : ...
case IPC_DISCONNECTION : ...
case IPC_MESSAGE: {
struct ipc_message *m = event.m;
...
}
}
}
## How libIPC works
3. send messages
```c
struct ipc_message m
m.payload = ...
m.length = strlen(m.payload)
m.fd = event.fd
m.type = ...
m.user_type = ...
ipc_write (context, &m)
```
#pause
4. add and remove fd from the context
ipc_add_fd (context, fd)
ipc_del_fd (context, fd)
## How libIPC works
LibIPC also helps to create "protocol daemons" like TCPd with
automatic switching between file descriptors
LibIPC takes callbacks to obtain libipc payloads inside arbitrary message structure
Example: websocketd.
Clients exchange data with a libipc service through websockets messages.
websocketd binds both the client and its service file descriptors,
then provides the libipc a callback to extract libipc messages from
the websocket messages sent by the client.
Same thing the other way.
ipc_switching_callbacks (context, client_fd, cb_in, cb_out)
## libIPC internal structures
Main goal: simplest possible structures
Examples:
Message
struct ipc_message {
char type; => Internal message type, used by protocol daemons.
char user_type; => User-defined message type (arbitrary).
int fd; => File descriptor concerned about this message.
uint32_t length; => Payload length.
char *payload; => Actual payload.
};
Context of the whole networking state
struct ipc_ctx {
struct ipc_connection_info *cinfos; => Keeps track of connections.
struct pollfd *pollfd; => List of "pollfd" structures within cinfos,
so we can pass it to poll(2).
size_t size; => Size of the connection list.
struct ipc_messages tx; => Messages to send.
struct ipc_switchings switchdb; => Relations between fd.
};
## Future of libIPC
LibIPC will be rewritten in Zig
* simpler to read and write
* data structures are simpler to create with comptime
* less code redundancy (more generic functions)
* already existing error management as written in current libIPC
* already existing loggin system
* no more C's pitfalls
* way better at exposing bugs
* thanks to a better type system
* way safer: cannot ignore errors
* so I won't
* simpler to cross-compile: same standard library for every OSs
* simpler (and more secure) memory management
Also: this won't change existing bindings! No excuses!
## Why not use it?
Current limitations
* performances (libIPC is based on poll(2), not epoll nor kqueue)
* it really isn't an issue until you have hundreds or thousands of clients
* parallelism is not permitted
nothing in libIPC is thread-safe
The future Zig implementation will overcome these issues.
## Questions?
Ask! `karchnu at karchnu.fr`