Writing a Resource Manager - Part 1 (3).pdf

Full Transcript

Writing a Resource Manager Introduction to Resource Managers l They are used as drivers in Neutrino. –They are accessible to anyone using the system. – No special arrangement is made with the kernel for a driver to run. (aside from namespace usage) What are some key benefi...

Writing a Resource Manager Introduction to Resource Managers l They are used as drivers in Neutrino. –They are accessible to anyone using the system. – No special arrangement is made with the kernel for a driver to run. (aside from namespace usage) What are some key benefits from using a resource manager? – Easy to test. l You can use command line utilities – Uniform design and portability. l All resource managers must follow POSIX interfaces. – Easy to start and stop l If a resource manager crashes it has no ill effect on the rest of the system. What Makes a Resource Manager l Here is a psudocode representation of a resource manager architecture. l initialize the dispatch interface l register the pathname with the process manager l DO forever Receive a message SWITCH on the type of message CASE io_open: perform io_open processing ENDCASE CASE io_read: perform io_read processing ENDCASE CASE io_write: perform io_write processing ENDCASE. // etc. handle all other messages. // that may occur, performing. // processing as appropriate ENDSWITCH ENDDO Architectural pattern… l This is the same architecture we followed when we created our basic drivers. – Setup pathname – Loop forever – Decode incoming messages and reply to them. l This can result in a lot of useless code to handle all of those different types of POSIX messages. l If only there was an easier way!...There is Resource Manager Libraries l Writing resource managers can seem like a very large task. – How will you handle every possible POSIX function. There could be dozens of cases to consider. – How will you handle POSIX requests you do not support. – If you create more then one resource manager you will quickly find a lot of code is reused from one resource manager to the next. l QNX Neutrino has you covered. Libraries exist which you can add to your resource manager and many of the common tasks which are mundane in the resource manager world will be taken care of. – The resource manager shared library allows you to take all the default actions for a function. – You just overload the functions you want to change. Example – Credit Cards l Lets take an example. You have created a resource manager to read and write credit cards. So in your mind the only POSIX operations you care about are. – read, write, open, close Since resource managers can be accessed by anyone what if someone did an lseek function call on your driver. Well you never setup to handle the lseek I/O message type. Since you are using the resource manager shared library you don’t have to worry. The default action prescribed in the library will be called. – This could be simply returning ENOSYS – Not supported – The library could use a default operation to handle the call. ie dup. Resource Manager Layers l When using the Resource Manager shared libraries there are many different layers that need to be setup. The library will do a lot of the work for us but we need to define what needs to happen. l Resource Manager Layers – Thread Pool Layer – Dispatch Layer – Resmgr Layer – Iofunc Layer Thread Pool Layer l This layer allows you to have either a single threaded resource manager or a multi-threaded one. – The resource manager library can and will maintain your multithreaded applications. – It handles load balancing between threads. – It also determines the optimal number of threads your resource manager can use. For now we will focus on single threaded resource managers. In the future we will discuss how to start a thread pool within your resource managers. Dispatch Layer l This is where you will define which events your resource manager cares about. – Pulses – I/O Messages – Other… You will also define how your resource manager should be alerted to these messages. – You will define a number of your own functions and associate them with events. – The dispatch structure will tie your function pointers to those events. – When an event is fired a simple look up in the dispatch structure will allow the proper user function to be called. – If a match between user functions and event is not found then the default function for that event is called. Resmgr Layer l You will register your namespace path here. This layer will tie the events in the dispatch layer to the functions defined in the iofunc layer. l Once this layer is called your resource manager, ready or not, is open for clients to communicate with. – Always make sure you are ready to receive messages when this layer is done. – If there is an error setting up your resource manager skip the functions which make up this layer. (resmgr_attach). If you didn’t use this layer you would have to parse the incoming message your self and create a very large switch statement to handle all the different message types. io_func Layer l This layer encompasses all the functions your resource manager is associated with. This includes your user defined functions. – You will create tables of function pointers for all POSIX messages. – Most of the messages will use default io_func functions. – Some of the default io_func functions will be overloaded with your own functions. It is worth noting you can always call a default io_func function within your own custom functions. – io_funct_verify – to verify the client sending the request has permission to access the resource. Steps to Create a Resource Manager l Lets go through the steps, functions and data structures required to setup a basic resource manager. l First create the dispatch structure: l dispatch_t *dpp; l dpp = dispatch_create(); l A dispatch structure is the glue your resource manager uses to keep everything together. To schedule the calling of your custom user functions. Add functions… l Next we setup two tables of functions. – Connect Functions l These are called as a result of POSIX calls that take a filename – open(path), unlink(path) – I/O Functions l These are called as a result of POSIX calls that take a file descriptor – read(fd), write(fd) The connection functions go in o resmgr_connect_funcs_t The I/O functiosn go in – resmgr_io_funcs_t …Add functions l typedef struct _resmgr_connect_funcs { unsigned nfuncs; int (*open) (); int (*unlink) ( …); int (*rename) (…); } resmgr_connect_funcs_t l typedef struct _resmgr_io_funcs { unsigned nfuncs; int (*read) (); int (*write) ( …); int (*stat) (…); } resmgr_io_funcs_t Shared structures l Using these structures… resmgr_connect_funcs_t connect_funcs; resmgr_io_funcs_t io_funcs; iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs, _RESMGR_IO_NFUNCS, &io_funcs); connect_funcs.open = io_open; io_funcs.read = io_read; io_funcs.write = io_write; Next steps… l iofunc_func_init – This function fills our tables with the default resource manager i/o functions. You should always call this so the default actions can be taken for POSIX message you do not handle. l We then overloaded the functions we desired with our user defined functions. l Next you will fill a device attributes structure. This structure defines the device our resource manager is going to manage. It also defines the mount points which will show up for the device under our namespace path we create. For now use the default attributes below. l iofunc_attr_t ioattr; l iofunc_attr_init(&ioattr, S_IFCHR | 0666, NULL, NULL) …Next Steps… l Next you will attach a resource manager to the pathname. This is done with the resmgr_attach function. You must be root for this function to work. l Id = resmgr_attach(dpp, &rattr, path, file_type, flags, &connect_funcs, &io_funcs, handle); l dpp – The return from dispatch_create l rattr – Further message parameters. Set to NULL for now. l path – The namespace path you want. l file_type – The message types we support. For now use default of _FTYPE_ANY. l flags – Control flags use 0 for now. l connect_funcs & io_funcs – defined later. l handle – Pointer to device attributes. l Return id – The id of the pathname registered. Used when resmgr_detach is called. …Next Steps l The last action we need to do is allocate a dispatch context. This is a data structure which contains all the relevent data needed by the message receive loop. – rcvid – buffers – message info struct. dispatch_context_t *ctp; ctp = dispatch_context_alloc (dpp); The message receive loop Our resource manager is now ready to service requests. The message receive loop has also been simplified by the resource manager library. while(1) { ctp = dispatch_block(ctp); Dispatch_handler(ctp); } l The dispatch_block function will do our MsgRecieve on our behalf. Store important msginfo. l The dispatch_handler function will use the function tables we created earlier to call the right function for the message type received. It will then reply back to the client program. Example of a Resource Manager An example of using a resource manager to control a device. A robot arm would make a good candidate for a resource manager. – You can test with command line utilities. The driver could register the name, /dev/robot/arm/angle, and any writes to this device are interpreted as the angle to set the robot arm to. To test the driver from the command line, you'd type: #echo 87 >/dev/robot/arm/angle The echo utility opens /dev/robot/arm/angle and writes the string (“87”) to it. The driver handles the write by setting the robot arm to 87 degrees. Note that this was accomplished without writing a special tester program. Another example would be names such as /dev/robot/registers/r1, r2,... Reading from these names returns the contents of the corresponding registers; writing to these names set the corresponding registers to the given values. Client Interaction l When a client tries to access a pathname owned by a resource manager first he needs to acquire a handler. This is done by first talking to the Process Manager to find out who the resource manager is and then by asking for a file handler from the resource manager. fd = open("/dev/ser1", O_RDWR); l Once a handler is established a client can read/write as they see fit with the resource manager. for (packet = 0; packet < npackets; packet++) { write(fd, packets[packet], PACKET_SIZE); } l Finally in the end letting the resource manager know when a handle can be destroyed. close(fd); Server’s Interaction l We will go over what messages a server sees next lecture.

Use Quizgecko on...
Browser
Browser