Set up a shared memory region between userspace and the kernel - c

I would like to create a shared memory mechanism between the kernel and 1 specific process in userspace. This process will spawn threads that will access this shared memory region.
Setting up the region between the userspace and the kernel does not have to be fast, but once it is done, no syscall should be made by the userspace process in order to write/read from the region and the region should be accessible by any kernel thread (not only the kernel part of the userspace process)
Ideally I would like to come up with something like:
Userspace allocates k bytes in its own address space using mmap() or posix_memalign(), where k is typically larger than a page.
Userspace sends the address returned by mmap() to the kernel using a kernel module or a syscall.
Kernel uses this address to map this region to its own address space so that it can see everything that gets written in this region also after the process exits the call. This means that the mapping needs to be done elsewhere in the kernel virtual address space
I have managed to come up with an example with a single page, with a kernel module doing something like:
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <linux/pagemap.h>
/*
* Prototypes
*/
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos);
static struct file_operations led_ops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_release,
.write = device_write
};
static char *my_page_address;
static struct page *kernel_page;
static ssize_t device_write(struct file *filp, const char __user *buf, s ize_t count, loff_t * ppos)
{
int res;
unsigned long uaddr;
char addrstr[80];
unsigned long copied = copy_from_user(addrstr, buf, count);
struct task_struct *task = current; // getting global current pointer
if (copied != 0)
{
pr_err("Error);
}
//Convert adress to long
uaddr = simple_strtoul(addrstr, NULL, 0);
uint64_t new_addr = uaddr;
printk(KERN_NOTICE "Module received the user adddress %p", uaddr);
//Locks the current process memory map
down_read(&current->mm->mmap_sem);
res = get_user_pages(
uaddr,
1, //resquest 1 page
1, //write enable
&kernel_page,
NULL);
if (res == 1) {
pr_err("Storing shared page");
my_page_address = kmap(kernel_page);
}
//Release the lock
up_read(&current->mm->mmap_sem);
return count;
}
Now, this seems to work for a single page. But several questions come to mind:
Can I extend this mechanism to a larger area than a page? Ideally I would like the kernel to access the memory region the same way the userspace process would do (just by having its own pointer in its own address space)
get_user_pages can take an array of pointers to vmas corresponding to each page as the last argument. If the userspace allocates a large contiguous buffer in its address space, can I use this vma to remap the same contiguous region in kernel space?
From what I have seen from other answers, some people recommend to allocate memory from within the kernel and allow users to mmap to it (For example here). But since the address space of the process is visible in the kernel space while the process is making the call, it should be possible to simply remap the part of the user address space to the kernel address space, right?
EDIT: Not a duplicate of this question. I want to know about a good way of mapping a userspace region to the kernel address space. Not accessing it via copy_from_user()

Related

Confused about mmap() return - two different pointers?

Currently trying to understand how memory mapping works in Linux (or in general, really), and I'm following with this one example of Shared Memory in POSIX systems from Operating System Concepts. The two files are as follows:
Producer file
// This is the producer file for the Shared memory object
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
int main()
{
// ---------------- PRODUCER ESTABLISHES SHARED MEMORY OBJECT AND WRITES TO IT ----------------
// Specifying the size in bytes of the shared memory object
const int SIZE = 4096;
// Name of the shared memory space
const char *name = "OS";
// The actual strings to write to shared memory
const char *message_0 = "Hello";
const char *message_1 = "World!";
// Shared memory file descriptor will be stored in this
int fd;
// Pointer to the shared memory object will be stored in this
char *ptr;
// Checking error
int errnum;
// Create a shared memory object. This opens (establishes a connection to) a shared memory object.
fd = shm_open(name, O_CREAT | O_RDWR,0666);
if (fd == -1)
{
errnum = errno;
fprintf(stdout, "Value of errno: %d\n", errno);
perror("Error printed by perror");
fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
return 1;
}
// Configure the size of the shared-memory object to be 4096 bytes
ftruncate(fd, SIZE);
// Memory map the shared memory object
ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("Ptr is: %p\n", ptr);
if (ptr == MAP_FAILED)
{
errnum = errno;
fprintf(stdout, "Value of errno in ptr: %d\n", errno);
perror("Error printed by perror");
fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
return 1;
}
// Write to the shared memory object
sprintf(ptr, "%s", message_0);
ptr += strlen(message_0);
sprintf(ptr, "%s", message_1);
ptr += strlen(message_1);
return 0;
}
Consumer file
// This is the consumer file for the Shared memory object, in which it reads what is in the memory object OS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
int main()
{
// Size in bytes of shared memory object
const int SIZE = 4096;
// Name of the shared memory space
const char *name = "OS";
// Shared memory file descriptor will be stored in this
int fd;
// Pointer to the shared memory object will be stored in this
char *ptr;
// Checking error
int errnum;
// Open the shared memory object
fd = shm_open(name, O_RDWR, 0666);
// If error in shm_open()
if (fd == -1)
{
errnum = errno;
fprintf(stdout, "Value of errno: %d\n", errno);
perror("Error printed by perror");
fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
return 1;
}
// Memory-map the shared memory object
ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("Ptr is: %p\n", ptr);
if (ptr == MAP_FAILED)
{
errnum = errno;
fprintf(stdout, "Value of errno in ptr: %d\n", errno);
perror("Error printed by perror");
fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
return 1;
}
// Read from the shared memory object
printf("%s\n", (char *) ptr);
// Remove the shared memory object (delete it)
shm_unlink(name);
return 0;
}
When I print the pointer to the shared memory object (printf("Ptr value: %p\n, ptr)), I get two different values for the consumer and producer files. Why does this happen?
As I understand, the pointer ptr points at the shared memory object, which is in the physical memory. This physical memory is just shared amongst two processes, by mapping it onto their address space. However, this would require that the pointer point to the same address in physical memory, no? Or is it pointing to the virtual memory (i.e. the address space of the processes)? If so, does that itself point to the physical memory?
Thanks!
Each process has its own virtual memory address space. The mappings from virtual memory to physical memory are, in general, different for each process. While the shared memory may be in physical memory at address 0x1000, the producer process may have it mapped into virtual memory at address 0x7000, and the consumer process may have it mapped into virtual memory at address 0x4000.
Further, if the shared memory is swapped out of memory for some reason, the system could later reload it to a different physical address, say 0x13000, and update the mappings in the processes so that it appears at the same addresses as before in each of the producer and consumer processes.
Producer and consumer are two different processes, so they have their own virtual memory space each. When you attach the shared segment, you specify the kernel that you have no preference on where (in your unallocated virtual address space) to put it, so you normally get different pointers because most probably both processes have different memory maps. Just think on an scenario where one process has allocated the memory map at, say, address A, and the other process has that address A occupied by some other thing it has allocated (e.g. dynamic memory for the heap, or a different shared library) It is clear that the kernel cannot allocate two different things in the same (virtual) address range (or even overlap some other mapping), so it uses a differen place, and returns a different pointer.
Just don't worry about it, as both virtual addresses map to the same phisical address (this is sure, as it is a shared memory segment), and the pointers finally end pointing to the same thing. The kernel is normally not aware of where it has placed that same segment in a different process, it doesn't need to do. This means that it is highly improbable that you get both pointers equal, contrary to what you though.
As none of these processes knows about the virtual address space of the other, there's no conflict, but never pass the address to the other process to use, because an address in a virtual address space has absolutely no meaning in the virtual address space of another, different process.
A process has normally no means (and no possibility) to know how the kernel assings the memory and builds the map of virtual to physicall addresses. Even for itself (the kernel also runs in it's virtual memory space, and only deals with the tables that map ---these tables are in the virtual address space of the kernel, but not in the virtual address space of any user process, so this is the reason that makes the kernel capable of changing the mappings but it is not possible for other processes)
It is impossible for a process to know where in actual memory a virtual address points to. That is possible for some administrator processes through a device (and a special device is needed for this) /dev/mem. But if you try to look in there without the actual mapping you'll get a mess of pages some belonging to a process, some to another, with no apparent structure, or you cannot also know what does it mean the contents of those pages.
There's another device /dev/kmem that maps to the kernel virtual address space, and allows a process (e.g. a debugger) to get access to the mapping table there.... but this is a very dangerous bend, as you can easily crash your system by tweaking there. Think that you cannot normally stop the kernel (this stops the system) or if you can do, you will stop essential parts of the system that should be running.

How to create Posix shared memory to work with integer

here is a bit of background on what I am trying to do for this class assignment. I am going to have 10 total programs that are going using semaphores to be synchronized to accessing a shared memory construct that is going to hold an Integer. This integer will hold the amount of text that has currently been read. How exactly am I am going to take this setup from my textbook for strings and convert it for use with Integers?
The 9 of the programs will only increment the value of the shared memory and then one of the programs will just read the shared memory value for later use. "Producer" is going to be the 9 programs that will increment the value of the shared memory. "Consumer" is going to be the single program that will read the value later on to do separate operations.
The "Consumer" program will be initializing the shared memory and setting the int value to 0.
Below is the "Producer" program that will build and write to the shared memory construct.
#include <stdio.h>
#include <stlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main()
{
/* the size (in bytes) of shared memory object */
const int SIZE 4096;
/* name of the shared memory object */
const char *name = "OS";
/* strings written to shared memory */
const char *message 0 = "Hello";
const char *message 1 = "World!";
/* shared memory file descriptor */
int shm fd;
/* pointer to shared memory obect */
void *ptr;
/* create the shared memory object */
shm fd = shm open(name, O CREAT | O RDRW, 0666);
/* configure the size of the shared memory object */
ftruncate(shm fd, SIZE);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT WRITE, MAP SHARED, shm fd, 0);
/* write to the shared memory object */
sprintf(ptr,"%s",message 0);
ptr += strlen(message 0);
sprintf(ptr,"%s",message 1);
ptr += strlen(message 1);
return 0;
}
Now below is the "Consumer" program that will read from the shared memory
#include <stdio.h>
#include <stlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main()
{
/* the size (in bytes) of shared memory object */
const int SIZE 4096;
/* name of the shared memory object */
const char *name = "OS";
/* shared memory file descriptor */
int shm fd;
/* pointer to shared memory obect */
void *ptr;
/* open the shared memory object */
shm fd = shm open(name, O RDONLY, 0666);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT READ, MAP SHARED, shm fd, 0);
/* read from the shared memory object */
printf("%s",(char *)ptr);
/* remove the shared memory object */
shm unlink(name);
return 0;
}
My idea of how to change this is to end up having the following changes for the Producer
change SIZE to 4 bytes for the size of Int
read the value of the int from shared memory assign that value to shmValue
shmValue then gets incremented
shmValue then gets written back to shared memory
My idea of how to change the Consumer program
change SIZE to 4 bytes for the size of Int
initialize the shared memory to int with a value of 0
reads shared memory to shmValue
then uses shmValue for later operations.
Again, guys, I would very much appreciate the help in dealing with this issue that I am having in understanding how to use this technology. Thank you for the help in advance!!

Why does printk() work only in the init/exit method of a kernel module? (Priority should be fine)

My goal is to write a kernel-module. I am following the memory tutorial of the freesoftware magazine.
The tutorial works fine. I am able to compile the code. When loaded with insmod, the kernel prints <1>Inserting memory module as expected. When I remove the module using rmmod the kernel prints <1>Removing memory module.
For debugging purposes, I am trying to add printk() to the other methods. But they are never printed.
The priority of all the messages is <1>.
I write into the device by: echo -n test1234 > /dev/memory
And use cat /dev/memory to get back the data.
cat /var/log/messages and dmesg donĀ“t print anymore information
[ 5550.651221] <1>Inserting memory module
[ 5550.655396] <1>Inserting memory module !!!!!!!!!!!!!!!
[12230.130847] <1>Removing memory module
cat /proc/sys/kernel/printk
7 4 1 7
uname- a
Linux generic-armv7a-hf 3.14.0-163850-g775a3df-dirty #2 SMP Mon Jan 12 13:53:50 CET 2015 armv7l GNU/Linux
Why does printk() only work in the init and exit method?
Is there any (better) way to print variable values than printk()?
Here the code:
/* Necessary includes for device drivers */
#include <linux/init.h>
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
MODULE_LICENSE("Dual BSD/GPL");
/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);
/* Structure that declares the usual file */
/* access functions */
struct file_operations memory_fops = {
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);
/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;
int memory_init(void) {
int result;
/* Registering device */
result = register_chrdev(memory_major, "memory", &memory_fops);
if (result < 0) {
printk(
"<1>memory: cannot obtain major number %d\n", memory_major);
return result;
}
/* Allocating memory for the buffer */
memory_buffer = kmalloc(1, GFP_KERNEL);
if (!memory_buffer) {
result = -ENOMEM;
goto fail;
}
memset(memory_buffer, 0, 1);
printk("<1>Inserting memory module\n"); ///this works fine
printk("<1>Inserting memory module !!!!!!!!!!!!!!!\n"); ///this works fine too
return 0;
fail:
memory_exit();
return result;
}
void memory_exit(void) {
/* Freeing the major number */
unregister_chrdev(memory_major, "memory");
/* Freeing buffer memory */
if (memory_buffer) {
kfree(memory_buffer);
}
printk("<1>Removing memory module\n"); //never printed
}
int memory_open(struct inode *inode, struct file *filp) {
printk("<1>memory open\n"); //never printed
/* Success */
return 0;
}
int memory_release(struct inode *inode, struct file *filp) {
printk("<1>memory_release\n"); //never printed
/* Success */
return 0;
}
ssize_t memory_read(struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
printk("<1>mem read\n"); //never printed
/* Transfering data to user space */
copy_to_user(buf,memory_buffer,1);
/* Changing reading position as best suits */
if (*f_pos == 0) {
*f_pos+=1;
return 1;
} else {
return 0;
}
}
ssize_t memory_write( struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
printk("<1>mem write\n"); //never printed
char *tmp;
tmp=buf+count-1;
copy_from_user(memory_buffer,tmp,1);
return 1;
}
Your driver seems fine, but you aren't actually talking to it with your test commands, so the functions with printks aren't being called. Once the module is loaded, it registers a major and minor number, 60 and 0 in your case. (Down the road you should update the module to request an available major number instead of using a hard-coded one.)
You need to create a file system node with mknod in order to actually use the driver. This will create the /dev/memory node and connect it to the module you have loaded. Then when it is opened, closed, read from, or written to, the file_operations in your module will be called, and the printks will work.
For your module, you should be able to use
mknod /dev/memory c 60 0
You can also chmod 666 /dev/memory to allow any user to use the device, rather than running as root all the time.
Here's a script based on the one I use with modules I develop:
#!/bin/sh
device="memory"
mode="666"
major=$(awk "\$2==\"$device\" {print \$1}" /proc/devices}
mknod /dev/${device} c $major 0
chmod $mode /dev/${device}
It will look up the major number associated with your module and create a file system node for it automatically.
Once you have loaded the module and run mknod or the script, you should be able to use the driver. You will know that it is working becase cat will only return the last character written to the device - your driver only has a one character buffer, and it is automatically overwritten each time a new character comes in. Then dmesg should show the printk's associated with the functions in your module.
The reason your driver seemed to work is because you were creating a regular file with your echo command, which cat happily printed right back to you. It's the same thing that would have happened if you ran those commands on a file in your home directory, you just happened to be in /dev instead.

Can anyone help me make a Shared Memory Segment in C

I need to make a shared memory segment so that I can have multiple readers and writers access it. I think I know what I am doing as far as the semaphores and readers and writers go...
BUT I am clueless as to how to even create a shared memory segment. I want the segment to hold an array of 20 structs. Each struct will hold a first name, an int, and another int.
Can anyone help me at least start this? I am desperate and everything I read online just confuses me more.
EDIT: Okay, so I do something like this to start
int memID = shmget(IPC_PRIVATE, sizeof(startData[0])*20, IPC_CREAT);
with startData as the array of structs holding my data initialized and I get an error saying
"Segmentation Fault (core dumped)"
The modern way to obtain shared memory is to use the API, provided by the Single UNIX Specification. Here is an example with two processes - one creates a shared memory object and puts some data inside, the other one reads it.
First process:
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#define SHM_NAME "/test"
typedef struct
{
int item;
} DataItem;
int main (void)
{
int smfd, i;
DataItem *smarr;
size_t size = 20*sizeof(DataItem);
// Create a shared memory object
smfd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0600);
// Resize to fit
ftruncate(smfd, size);
// Map the object
smarr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, smfd, 0);
// Put in some data
for (i = 0; i < 20; i++)
smarr[i].item = i;
printf("Press Enter to remove the shared memory object\n");
getc(stdin);
// Unmap the object
munmap(smarr, size);
// Close the shared memory object handle
close(smfd);
// Remove the shared memory object
shm_unlink(SHM_NAME);
return 0;
}
The process creates a shared memory object with shm_open(). The object is created with an initial size of zero, so it is enlarged using ftruncate(). Then the object is memory mapped into the virtual address space of the process using mmap(). The important thing here is that the mapping is read/write (PROT_READ | PROT_WRITE) and it is shared (MAP_SHARED). Once the mapping is done, it can be accessed as a regular dynamically allocated memory (as a matter of fact, malloc() in glibc on Linux uses anonymous memory mappings for larger allocations). Then the process writes data into the array and waits until Enter is pressed. Then it unmaps the object using munmap(), closes its file handle and unlinks the object with shm_unlink().
Second process:
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#define SHM_NAME "/test"
typedef struct
{
int item;
} DataItem;
int main (void)
{
int smfd, i;
DataItem *smarr;
size_t size = 20*sizeof(DataItem);
// Open the shared memory object
smfd = shm_open(SHM_NAME, O_RDONLY, 0600);
// Map the object
smarr = mmap(NULL, size, PROT_READ, MAP_SHARED, smfd, 0);
// Read the data
for (i = 0; i < 20; i++)
printf("Item %d is %d\n", i, smarr[i].item);
// Unmap the object
munmap(smarr, size);
// Close the shared memory object handle
close(smfd);
return 0;
}
This one opens the shared memory object for read access only and also memory maps it for read access only. Any attempt to write to the elements of the smarr array would result in segmentation fault being delivered.
Compile and run the first process. Then in a separate console run the second process and observe the output. When the second process has finished, go back to the first one and press Enter to clean up the shared memory block.
For more information consult the man pages of each function or the memory management portion of the SUS (it's better to consult the man pages as they document the system-specific behaviour of these functions).

Using system calls from C, how do I get the utilization of the CPU(s)?

In C on FreeBSD, how does one access the CPU utilization?
I am writing some code to handle HTTP redirects. If the CPU load goes above a threshold on a FReeBSD system, I want to redirect client requests. Looking over the man pages, kvm_getpcpu() seems to be the right answer, but the man pages (that I read) don't document the usage.
Any tips or pointers would be welcome - thanks!
After reading the answers here, I was able to come up with the below. Due to the poor documentation, I'm not 100% sure it is correct, but top seems to agree. Thanks to everyone who answered.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>
#define CP_USER 0
#define CP_NICE 1
#define CP_SYS 2
#define CP_INTR 3
#define CP_IDLE 4
#define CPUSTATES 5
int main()
{
long cur[CPUSTATES], last[CPUSTATES];
size_t cur_sz = sizeof cur;
int state, i;
long sum;
double util;
memset(last, 0, sizeof last);
for (i=0; i<6; i++)
{
if (sysctlbyname("kern.cp_time", &cur, &cur_sz, NULL, 0) < 0)
{
printf ("Error reading kern.cp_times sysctl\n");
return -1;
}
sum = 0;
for (state = 0; state<CPUSTATES; state++)
{
long tmp = cur[state];
cur[state] -= last[state];
last[state] = tmp;
sum += cur[state];
}
util = 100.0L - (100.0L * cur[CP_IDLE] / (sum ? (double) sum : 1.0L));
printf("cpu utilization: %7.3f\n", util);
sleep(1);
}
return 0;
}
From the MAN pages
NAME
kvm_getmaxcpu, kvm_getpcpu -- access per-CPU data
LIBRARY
Kernel Data Access Library (libkvm, -lkvm)
SYNOPSIS
#include <sys/param.h>
#include <sys/pcpu.h>
#include <sys/sysctl.h>
#include <kvm.h>
int
kvm_getmaxcpu(kvm_t *kd);
void *
kvm_getpcpu(kvm_t *kd, int cpu);
DESCRIPTION
The kvm_getmaxcpu() and kvm_getpcpu() functions are used to access the
per-CPU data of active processors in the kernel indicated by kd. The
kvm_getmaxcpu() function returns the maximum number of CPUs supported by
the kernel. The kvm_getpcpu() function returns a buffer holding the per-
CPU data for a single CPU. This buffer is described by the struct pcpu
type. The caller is responsible for releasing the buffer via a call to
free(3) when it is no longer needed. If cpu is not active, then NULL is
returned instead.
CACHING
These functions cache the nlist values for various kernel variables which
are reused in successive calls. You may call either function with kd set
to NULL to clear this cache.
RETURN VALUES
On success, the kvm_getmaxcpu() function returns the maximum number of
CPUs supported by the kernel. If an error occurs, it returns -1 instead.
On success, the kvm_getpcpu() function returns a pointer to an allocated
buffer or NULL. If an error occurs, it returns -1 instead.
If either function encounters an error, then an error message may be
retrieved via kvm_geterr(3.)
EDIT
Here's the kvm_t struct:
struct __kvm {
/*
* a string to be prepended to error messages
* provided for compatibility with sun's interface
* if this value is null, errors are saved in errbuf[]
*/
const char *program;
char *errp; /* XXX this can probably go away */
char errbuf[_POSIX2_LINE_MAX];
#define ISALIVE(kd) ((kd)->vmfd >= 0)
int pmfd; /* physical memory file (or crashdump) */
int vmfd; /* virtual memory file (-1 if crashdump) */
int unused; /* was: swap file (e.g., /dev/drum) */
int nlfd; /* namelist file (e.g., /kernel) */
struct kinfo_proc *procbase;
char *argspc; /* (dynamic) storage for argv strings */
int arglen; /* length of the above */
char **argv; /* (dynamic) storage for argv pointers */
int argc; /* length of above (not actual # present) */
char *argbuf; /* (dynamic) temporary storage */
/*
* Kernel virtual address translation state. This only gets filled
* in for dead kernels; otherwise, the running kernel (i.e. kmem)
* will do the translations for us. It could be big, so we
* only allocate it if necessary.
*/
struct vmstate *vmst;
};
I believe you want to look into 'man sysctl'.
I don't know the exact library, command, or system call; however, if you really get stuck, download the source code to top. It displays per-cpu stats when you use the "-P" flag, and it has to get that information from somewhere.

Resources