Edit: I have found seq_file
that eases writing a lot of data from kernel to user-space. What I am looking for is the opposite; an API that facilitates reading a lot of data (more than one page) from user-space.
Edit 2: I am implementing a port of <stdio.h>
as a kernel module that would be able to open /proc
(and later, other virtual file systems) similar to FILE
s and handle input and output similar to <stdio.h>
. You can find the project here.
I have found a LOT of questions on how the kernel can write large amounts of data to /proc (for user-space programs to take), but nothing for the other way around. Let me elaborate:
This question is basically about the algorithm by which the input is tokenized (for example to int
s or a mixture of int
and string etc), given that the data maybe broken between multiple buffers.
For example, imagine the following data is being sent to the kernel module:
12345678 81234567 78123456 67812345 5678 1234 45678123 3456 7812 23456781
and for the sake of this example, let's say the page size by which Linux feeds the /proc handler is 20 bytes (vs the real 4KB).
The function that reads the data from /proc (in the kernel module) then sees the data as such:
call 1:
"12345678 81234567 78"
call 2:
"123456 67812345 5678"
call 3:
" 1234 45678123 3456 "
call 4:
"7812 23456781"
As you can see, when 78
is read in the first call, it shouldn't be processed yet until the next frames for it to decide whether 78
was a whole number or one cut between frames.
Now I found seq_file
s that apparently are only for when the kernel wants to write data to user rather than read (or it could be that the HOWTO is horribly written).
So far, I have come with the following solution (I am writing from memory, so I may miss a couple error checkings, but bear with me):
In the initialization phase (say init_module
):
initialize mutex1 to 1 and mutex2 to 0
create /proc entry
call data_processor
/proc reader:
1. down(mutex1) /* down_interruptible of course, but let's not get into details */
2. copy_from_user to an internal buffer
buffer_index = 0
data_length = whatever the size is
3. strip spaces from end of buffer (except if all left from buffer is 1 space)
if so, there_was_space_after = 1 else 0
4. up(mutex2)
I will explain why I strip spaces later
get_int
function:
wait_for_next = 0
number_was_cut = 0
last_number = 0
do
{
1. down(mutex2)
2. if (number_was_cut && !isdigit(buffer[buffer_index]))
break /* turns out it wasn't really cut
as beginning of next buffer is ' ' */
number_was_cut = 0
wait_for_next = 0
3. while (buffer_index < data_length && !isdigit(buffer_index[buffer_index]))
++buffer_index; /* skip white space */
4. while (buffer_index < data_length && isdigit(buffer[buffer_index]))
last_number = last_number * 10 + buffer[buffer_index++] - '0';
5. if (buffer_index >= data_length && !there_was_space_after)
number_was_cut = 1
wait_for_next = 1
up(mutex1) /* let more data come in */
else
up(mutex2) /* let get_int continue */
break
} while (wait_for_next)
return last_number
data_processor
function (for example):
int first_num = get_int()
int sencod_num = get_int()
for i = first_num to second_num
do_whatever(get_int())
Explanation: First, see data_processor
. It doesn't get involved in complications on how the data are read, so it just gets integers and does whatever it wants with them. Now let's see /proc reader. It basically waits for data_processor
to call get_int
enough times for all current data to be consumed (step 1) and then copies the next buffer into internal memory, allowing data_processor
to continue (step 2). It then needs to strip trailing spaces so get_int
could be simplified a bit (step 3). Finally, it signals get_int
that it can start reading the data (step 4).
The get_int
function first waits for data to arrive (step 1), (ignore step 2 for now) it skips any unwanted characters (step 3) and then starts reading the number (step 4). The end of reading the number is by two possibilities; the end of buffer is reached (in which case, if /proc reader had not stripped any spaces, then the number could be cut between frames) or white space is met. In the former case, it needs to signal /proc reader to read in more data and wait for another cycle to append the rest of the number to the current one and in the later case, it returns the number (step 5). If continuing from last frame, check to see if new frame starts with a number or not. If not, then previous number was actually a whole number and should be returned. Otherwise, it needs to continue appending digits to last number (step 2).
The main problem with this method is that it is overly complicated. It gets much more complicated when get_string
is added, or the read integer could be hex etc. Basically, you have to reinvent sscanf
! Note that, sscanf
could be used in this simple example at step 4 of get_int
instead of the while
loop (or also with get_string
, but that gets more tricky when hex input is also possible (imagine the hex number being cut between 0 and x0212ae4). Even so, it just replaces step 4 of get_int
and the rest of the stuff should still remain.
It actually got me many bugs and heavy testing to perfect all the special cases. That's another reason why it doesn't look elegant to me.
I would like to know if there is any better method to handle this. I am aware that using shared memory could be an option, but I'm looking for an algorithm for this task (more out of curiosity since I already have my working solution). More specifically:
FILE
from which you can take data and it handles the breaking of data into pages itself?fscanf
faces a similar problem. How is this handled by that?Side question: Is it a terrible thing that I'm blocking the /proc reader on a mutex? I mean, writing data can be blocking, but I'm not sure if that normally happens in user-space or kernel-space.
create_proc_entry() Both of these functions are defined in the file linux/proc_fs. h. The create_proc_entry is a generic function that allows to create both read as well as write entries. create_proc_read_entry is a function specific to create only read entries.
To create a virtual file in the /proc filesystem, use the create_proc_entry function. This function accepts a file name, a set of permissions, and a location in the /proc filesystem in which the file is to reside.
The proc entries can also be created such that we can write data into the proc entry, the data in turn can be used for configuring modules in the kernel.
The request_firmware() interface may be of interest to you; the whole thing gets buffered by the kernel before it's handed to you.
Otherwise, maybe the sysfs binary attributes interface is more useful than proc?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With