Sorry to be blunt, Chris. But I think you're headed down a useless rat hole.
I agree that the usage of /proc you've described is a bad interface. I am slightly mystified as to how that came to be what you settled on. I don't think it's worthwhile to hash over that. Let's move on. Please forget ptrace. Please forget about adding syscalls. At this point I think I just need you to give me the benefit of the doubt when I tell you I am sure this is not the way, and even dabbling sidetracks us from really useful progress. Let's move on. To reiterate: transport is boring. This is not where the there is. We need to focus on what we're providing that anyone could care to have a means to communicate with! I'm pretty sure what most people had in mind when saying "fd-based" is approximately this: you do one open call, that fd is your handle on one session with the interface. You can write/ioctl to send it commands, and you can read (and perhaps ioctl) on it to receive either information replies or event notifications. You can use poll/select et al for efficient wakeup when there is something the interface wants to tell you. As with all fds, sudden death of the holder of the fd implicitly closes it and that triggers cleanup of the session on the kernel side. There are more possibilities with multiple fds used together as one "session with the interface", but this is the basic one. Let's start with that focus and move on. Below is a trivial example I whipped up. This uses debugfs. There are myriad ways to do the same thing; this one is simple and handy. I hope it illustrates how a magic file can set up per-open data structures for its operations to use, and tear them down gracefully and automatically on close. It should be clear how to add .write, .poll, .unlocked_ioctl, etc. functions to build a full transport this way without much hair. This method is the easy path to start with, and we can worry more about transport later. Let's move on. The way to find the easy route is to ask for directions. When it seems like there ought to be a more natural way to do something, there might well be some ways. When you post here about what you're up against, people here can give you pointers to what might help. It's not like posting on LKML and braving the flames. Noone here is going to give you a hard time (not worse than this, anyway). We need your participation to be able to share what we know. Let's move on together. I'm sorry I've been remiss in writing up everything I've promised about what I think we do need to focus on. It hasn't helped to get sidetracked on what I say is the boring stuff. But I recognize that my disorganization makes it difficult on your end. Thanks, Roland ----- /* * Compile the module and insmod it. * Be sure to have done "mount -t debugfs debugfs /sys/kernel/debug" * (systemtap or something else might already have done it for you). * Look at /sys/kernel/debug/foo for example behavior. */ #include <linux/sched.h> #include <linux/module.h> #include <linux/debugfs.h> #include <asm/atomic.h> MODULE_DESCRIPTION("example using debugfs"); MODULE_LICENSE("GPL"); static atomic_t next = ATOMIC_INIT(0); static int example_open(struct inode *inode, struct file *file) { /* * The 'struct file' describes what the user's file descriptor * from this one open points to. It's shared by dup and fd * inheritance, but not by an independent open call. We can * stash here some data specific to just this one open file * description. */ file->private_data = (void *) (unsigned long) atomic_inc_return(&next); return 0; } static ssize_t example_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned long n = (unsigned long) file->private_data; char string[128]; int size = snprintf(string, sizeof string, "you are number %lu\n", n); return simple_read_from_buffer(buf, count, ppos, string, size); } static int example_release(struct inode *inode, struct file *file) { unsigned long n = (unsigned long) file->private_data; printk(KERN_NOTICE "bye bye number %lu\n", n); return 0; } static const struct file_operations example_fops = { .owner = THIS_MODULE, .open = example_open, .release = example_release, .read = example_read }; static struct dentry *top_dentry; static int __init init_example(void) { struct dentry *d = debugfs_create_file("foo", 0666, NULL, NULL, &example_fops); if (!d) return -EROFS; if (IS_ERR(d)) return PTR_ERR(d); top_dentry = d; return 0; } static void __exit exit_example(void) { debugfs_remove(top_dentry); } module_init(init_example); module_exit(exit_example);