The Kernel Kit Table of Contents | The Kernel Kit Index |
Example 1: Creating and Writing into an Area
Example 2: Reading a File into an Area
Example 3: Accessing a Designated Area
Example 4: Cloning and Sharing an Area
As a simple example of area creation and usage, here we create a ten page area and fill half of it (with nonsense) by bumping a pointer:
area_id my_area; char *area_addr, *ptr; /* Create an area. */ my_area = create_area("my area", /* name you give to the area */ (void *)&area_addr, /* returns the starting addr */ B_ANY_ADDRESS, /* area can start anywhere */ B_PAGE_SIZE*10, /* size in bytes */ B_NO_LOCK, /* Lock in RAM? No. */ B_READ_AREA | B_WRITE_AREA); /* permissions */ /* check for errors */ if (my_area < 0) { printf("Something bad happenedn"); return; } /* Set ptr to the beginning of the area. */ ptr = area_addr; /* Fill half the area (with random-ish data). */ for (int i; i < B_PAGE_SIZE*5; i++) *ptr++ = system_time()%256;
You can also memcpy() and strcpy() into the area:
/* Copy the first half of the area into the second half. */ memcpy(ptr, area_addr, B_PAGE_SIZE*5); /* Overwrite the beginning of the area. */ strcpy(area_addr, "Hey, look where I am.");
When we're all done, we delete the area:
delete_area(my_area);
Here's a function that finds a file, opens it (implicit in the BFile constructor), and copies its contents into RAM:
#include <File.h> area_id file_area; status_t file_reader(const char *pathname) { status_t err; char *area_addr; BFile file(pathname, B_READ_ONLY); if ((err=file.InitCheck()) != B_OK) { printf("%s: Can>t find or open.n", pathname); return err; } err = file.GetSize(&file_size); if (err != B_OK || file_size == 0) { printf("%s: Disappeared? Empty?n", pathname); return err; } /* Round the size up to the nearest page. */ file_size = (((file_size-1)%B_PAGE_SIZE)+1)*B_PAGE_SIZE; /* Make sure the size won>t overflow a size_t spec. */ if (file_size >= ((1<<32)-1) ) { printf("%s: What'd you do? Read Montana?n"); return B_NO_MEMORY; } file_area = create_area("File area", (void *)&area_addr, B_ANY_ADDRESS, file_size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); /* Check create_area() errors, as in the last example. */ ... /* Read the file; delete the area if there>s an error. */ if ((err=file.Read(area_addr, file_size)) < B_OK) { printf("%s: File read error.n"); delete_area(file_area); return err; } /* The file is automatically closed when the stack-based * BFile is destroyed. */ return B_OK; }
In the previous example, a local variable (area_addr) was used to capture the starting address of the newly-created area. If some other function wants to access the area, it must "re-find" the starting address (and the length of the area, for boundary checking). To do this, you call get_area_info().
In the following example, an area is passed in by name; the function, which will write its argument buffer to the area, calls get_area_info() to determine the start and extent of the area, and also to make sure that the area is part of this team. If the area was created by some other team, the function could still write to it, but it would have to clone the area first (cloning is demonstrated in the next example).
status_t write_to_area(const char *area_name, const void *buf, size_t len) { area_id area; area_info ai; thread_id thread; thread_info ti; status_t err; if (!area_name) return B_BAD_VALUE; area = find_area(area_name); /* Did we find it? */ if (area < B_OK) { printf("Couldn>t find area %s.n", area_name); return err; } /* Get the info. */ err = get_area_info(area, &ai); if (err < B_OK) { printf("Couldn>t get area info.n"); return err; } /* Get the team of the calling thread; to do this, we have * to look in the thread_info structure. */ err = get_thread_info(find_thread(NULL), &ti); if (err < B_OK) { printf("Couldn>t get thread info.n"); return err; } /* Compare this team to the area>s team. */ if (ai.team != ti.team) printf("Foreign area.n"); return B_NOT_ALLOWED; } /* Make sure we>re not going to overflow the area, * and make sure this area can be written to. */ if (len > ai.size) { printf("Buffer bigger than area.n"); return B_BAD_VALUE; } if (!(ai.protection & B_WRITE_AREA)) { printf("Can>t write to this area.n"); return B_NOT_ALLOWED; } /* Now we can write. */ memcpy(ai.address, buf, len); return B_OK; }
It's important that you only write to areas that were created or cloned within the calling team. The starting address of a "foreign" area is usually meaningless within your own address space.
You don't have to check the area's protectection before writing to it (or reading from it). The memory-accessing fucntions (memcpy(), in this example) will segfault if an invalid read or write is requested.
In the following example, a server and a client are set up to share a common area. Here's the server:
/* Server side */ class AServer { status_t make_shared_area(size_t size); area_id the_area; char *area_addr; }; status_t AServer::make_shared_area(size_t size) { /* The size must be rounded to a page. */ size = ((size % B_PAGE_SIZE)+1) * B_PAGE_SIZE; the_area = create_area("server area", (void *)&area_addr B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA); if (the_area < B_OK) { printf("Couldn>t create server arean"); return the_area; return B_OK; }
And here's the client:
/* Client side */ class AClient { status_t make_shared_clone(); area_id the_area; char *area_addr; }; status_t AClient::make_shared_clone() { area_id src_area; src_area = find_area("server area"); if (src_area < B_ERROR) { printf("Couldn>t find server area.n"); return src_area; } the_area = clone_area("client area", (void *)&area_addr, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, src_area); if (the_area < B_OK) printf("Couldn>t create clone arean"); return the_area; } return B_OK; }
Notice that the area creator (the server in the example) doesn't have to designate the created area as sharable. All areas are candidates for cloning.
After it creates the cloned area, the client's area_id value (AClient::the_area) will be different from the server's (AServer::the_area). Even though area_id numbers are global, the client should only refer to the server's area_id number in order to clone it. After the clone, the client talks to the area through its own area_id (the value passed backed by clone_area()).
It's sometimes useful for shared areas (in other words, a "source" and a clone) to begin at the same starting address. For example, if a client's clone area starts at the same address as the server's original area, then the client and server can pass area-accessing pointers back and forth without having to translate the addresses. Here we modify the previous example to do this:
status_t AClient::make_shared_clone() { area_id src_area; src_area = find_area("server area"); if (src_area < B_ERROR) { printf("Couldn>t find server area.n"); return B_BAD_VALUE; } /* This time, we specify the address that we want the * clone to start at. The B_CLONE_ADDRESS constant * does this for us. */ area_addr = src_info.address; the_area = clone_area("client area", (void *)&area_addr, B_CLONE_ADDRESS, B_READ_AREA | B_WRITE_AREA, src_area); if (the_area < B_OK) printf("Couldn>t create clone arean"); return the_area; } return B_OK; }
Of course, demanding that an area begin at a specific address can be too restrictive; if any of the memory within [area_addr, area_addr + src_info.size] is already allocated, the clone will fail.
The Kernel Kit Table of Contents | The Kernel Kit Index |
Copyright © 2000 Be, Inc. All rights reserved..