diff --git a/ebpfcat/arraymap.py b/ebpfcat/arraymap.py
index 288869d6fa32d4d977a3680e08ed7b76b238ef11..226716d4f4ba44f371520740d0eabb44d38a82ac 100644
--- a/ebpfcat/arraymap.py
+++ b/ebpfcat/arraymap.py
@@ -70,8 +70,9 @@ class ArrayGlobalVarDesc(MemoryDesc):
 
 class ArrayMapAccess:
     """This is the array map proper"""
-    def __init__(self, data, size):
-        self.data = data
+    def __init__(self, fd, size):
+        self.data = mmap(fd, size)
+        self.fd = fd
         self.size = size
 
 
@@ -99,14 +100,14 @@ class ArrayMap(Map):
     def __set_name__(self, owner, name):
         self.name = name
 
-    def init(self, ebpf):
+    def init(self, ebpf, fd):
         setattr(ebpf, self.name, 0)
         size = self.collect(ebpf)
         if not size:  # nobody is actually using the map
             return
-        fd = create_map(MapType.ARRAY, 4, size, 1, MapFlags.MMAPABLE)
-        data = mmap(fd, size)
-        setattr(ebpf, self.name, ArrayMapAccess(data, size))
+        if fd is None:
+            fd = create_map(MapType.ARRAY, 4, size, 1, MapFlags.MMAPABLE)
+        setattr(ebpf, self.name, ArrayMapAccess(fd, size))
         with ebpf.save_registers(list(range(6))), ebpf.get_stack(4) as stack:
             ebpf.mI[ebpf.r10 + stack] = 0
             ebpf.r1 = ebpf.get_fd(fd)
diff --git a/ebpfcat/bpf.py b/ebpfcat/bpf.py
index ee07c4d42dda2e76c4ac9b7bda713a6b6c8eb2bb..26e11213350b16044c342f43d8c6dffd6adb26b7 100644
--- a/ebpfcat/bpf.py
+++ b/ebpfcat/bpf.py
@@ -150,6 +150,15 @@ def prog_load(prog_type, insns, license,
     else:
         return fd
 
+def obj_pin(pathname, fd):
+    pn = pathname.encode("utf8")
+    bpf(6, "QI", addrof(pn), fd)
+
+def obj_get(pathname):
+    pn = pathname.encode("utf8")
+    fd, _ = bpf(7, "Q", addrof(pn))
+    return fd
+
 def prog_test_run(fd, data_in, data_out, ctx_in, ctx_out,
                   repeat=1):
     if isinstance(data_in, int):
diff --git a/ebpfcat/ebpf.py b/ebpfcat/ebpf.py
index 879759f6bd15312360a94fb0190ad7af1974956f..c53420f1ac42b6d889bbdb8496f7739ae562b62b 100644
--- a/ebpfcat/ebpf.py
+++ b/ebpfcat/ebpf.py
@@ -1082,6 +1082,9 @@ class MemoryMap:
 
 class Map(ABC):
     """The base class for all maps"""
+    def __set_name__(self, owner, name):
+        self.filename = name
+
     @abstractmethod
     def init(self, ebpf):
         """create the map and initialize its values"""
@@ -1213,15 +1216,25 @@ class TemporaryDesc(RegisterDesc):
 class EBPF:
     """The base class for all EBPF programs
 
-    This class may even be instantiated directly, in which case you
-    can just issue the program before the it is loaded.
+    Usually this class is sub-classed, and the actual program is defined
+    in the overwritten `program` method. Then the program may be loaded into
+    the kernel. Alternatively, this class may even be instantiated directly,
+    in which case you can just issue the program before it is loaded.
+
+    After a program is loaded, its maps may be written to a bpf file system
+    using `pin_maps`. Those maps may be used at a later time, especially also
+    in a different task, if the parameter `load_maps` is given, in which case
+    we assume the program has already been loaded.
+
+    :param load_maps: a prefix to load pinned maps from. Must be existing in a
+        bpf file system, and usually ends in a "/".
     """
     stack = 0
     name = None
     license = None
 
     def __init__(self, prog_type=0, license=None, kern_version=0,
-                 name=None, subprograms=()):
+                 name=None, load_maps=None, subprograms=()):
         self.opcodes = []
         self.prog_type = prog_type
         if license is not None:
@@ -1232,7 +1245,7 @@ class EBPF:
                 self.name = self.__class__.__name__[:16]
         else:
             self.name = name
-        self.loaded = False
+        self.loaded = load_maps is not None
 
         self.mB = MemoryMap(self, "B")
         self.mH = MemoryMap(self, "H")
@@ -1257,9 +1270,23 @@ class EBPF:
         for p in subprograms:
             p.ebpf = self
 
-        for v in self.__class__.__dict__.values():
+        for k, v in self.__class__.__dict__.items():
+            if isinstance(v, Map):
+                if load_maps is None:
+                    v.init(self, None)
+                else:
+                    v.init(self, bpf.obj_get(load_maps + k))
+
+    def pin_maps(self, path):
+        """pin the maps of this program to files with prefix `path`
+
+        This path must be in a bpf file system, and all parent
+        directories must already exist, while the individual files
+        must not exist.
+        """
+        for k, v in self.__class__.__dict__.items():
             if isinstance(v, Map):
-                v.init(self)
+                bpf.obj_pin(path + k, getattr(self, v.name).fd)
 
     def program(self):
         """overwrite this method with your program while subclassing"""
diff --git a/ebpfcat/hashmap.py b/ebpfcat/hashmap.py
index 126f0c7746d91b00218b0bfa19baf48c2e852428..555ba4434685a50a401bb0b46068429fb0cfc2ed 100644
--- a/ebpfcat/hashmap.py
+++ b/ebpfcat/hashmap.py
@@ -99,8 +99,9 @@ class HashMap(Map):
         self.vars.append(ret)
         return ret
 
-    def init(self, ebpf):
-        fd = create_map(MapType.HASH, 1, 8, self.count)
+    def init(self, ebpf, fd):
+        if fd is None:
+            fd = create_map(MapType.HASH, 1, 8, self.count)
         for v in self.vars:
             getattr(ebpf, v.name).fd = fd
 
diff --git a/ebpfcat/xdp.py b/ebpfcat/xdp.py
index 7a9a0be06c03a0c18a8fb7e789c688214ea47d01..d38fe93ee4514386c737fbbcb80d225890dbe860 100644
--- a/ebpfcat/xdp.py
+++ b/ebpfcat/xdp.py
@@ -244,13 +244,13 @@ class XDP(EBPF):
         await self._netlink(ifindex, fd, flags)
 
     async def detach(self, network, flags=XDPFlags.SKB_MODE):
-        """attach this program from a ``network``
+        """detach this program from a ``network``
 
         :param network: the name of the network interface,
            like ``"eth0"``
         :param flags: one of the :class:`XDPFlags` """
         ifindex = if_nametoindex(network)
-        await self._netlink(ifindex, -1)
+        await self._netlink(ifindex, -1, flags)
 
     @asynccontextmanager
     async def run(self, network, flags=XDPFlags.SKB_MODE):
diff --git a/examples/ipcounter.py b/examples/ipcounter.py
index 0f41aaca71d426ddda5b5caa4d3f6d67f9b447b6..40545192244eac9060317f0c0cfc0dbd56a2e080 100644
--- a/examples/ipcounter.py
+++ b/examples/ipcounter.py
@@ -1,5 +1,6 @@
 """example program to count IPv4 and IPv6 packets"""
 
+from argparse import ArgumentParser
 from asyncio import get_event_loop, sleep
 from ebpfcat.arraymap import ArrayMap
 from ebpfcat.xdp import PacketVar, XDP, XDPExitCode, XDPFlags
@@ -22,13 +23,46 @@ class IPCount(XDP):
         self.exit(XDPExitCode.PASS)
 
 
+async def show(counter):
+    for i in range(10):
+        await sleep(0.1)
+        print(f"IPv4 {counter.ipv4count} IPv6 {counter.ipv6count}")
+
+
 async def main():
-    c = IPCount()
+    parser = ArgumentParser(
+        prog="ipcount",
+        description="Count IPv4 and IPv6 packets")
+
+    parser.add_argument("interface",
+                        help="the network interface to listen to")
+    parser.add_argument("-a", "--attach", action="store_true",
+                        help="attach the bpf program to the interface")
+    parser.add_argument("-s", "--show", action="store_true",
+                        help="show the number of received packets")
+    parser.add_argument("-d", "--detach", action="store_true",
+                        help="detach the bpf program from the interface")
+    args = parser.parse_args()
+
+    if args.attach or not args.show:
+        c = IPCount()
+    else:
+        c = IPCount(load_maps="/sys/fs/bpf/ipcount/")
 
-    async with c.run("eth0", XDPFlags.DRV_MODE):
-        for i in range(10):
-            await sleep(0.1)
-            print(f"packets arrived: IPv4 {c.ipv4count} IPv6 {c.ipv6count}")
+    if args.attach and args.detach:
+        async with c.run(args.interface, XDPFlags.SKB_MODE):
+            if args.show:
+                await show(c)
+    elif args.attach:
+        await c.attach(args.interface, XDPFlags.SKB_MODE)
+        c.pin_maps("/sys/fs/bpf/ipcount/")
+        if args.show:
+            await show(c)
+    else:
+        if args.show:
+            await show(c)
+        if args.detach:
+            await c.detach(args.interface, XDPFlags.DRV_MODE)
 
 
 if __name__ == "__main__":