This is an appendix to
Programming written for people interested in Ada programming on POSIX
Here is a first draft of
Compile it like this:
% gnatmake -P who1
Notice that we have switched to using a project
file. We do that to keep the extra command line arguments to
gnatmake in one file.
Run the program, and compare it with the system version of
% ./who1 [...] % who [...] %
Here is the finished version of
Compile and test this version:
% gnatmake -P who2 [...] % ./who2 sparre pts/1 Jan 15 15:28 (:20.0) sparre pts/2 Jan 15 15:29 (:20.0) sparre pts/0 Jan 15 15:35 (:20.0) sparre pts/3 Jan 15 15:34 (:20.0) sparre pts/4 Jan 16 06:51 (:20.0) sparre :20 Jan 13 13:25 % who sparre pts/1 2007-01-15 15:28 (:20.0) sparre pts/2 2007-01-15 15:29 (:20.0) sparre pts/0 2007-01-15 15:35 (:20.0) sparre pts/3 2007-01-15 15:34 (:20.0) sparre pts/4 2007-01-16 06:51 (:20.0) sparre :20 2007-01-13 13:25 %
Finally, here is code that implements the picture: cp1.adb
Compile and test the program:
% gnatmake -P cp1 [...] % ./cp1 cp1 copy.of.cp1 % ls -l cp1 copy.of.cp1 -rw-r--r-- 1 sparre sparre 41061 2007-01-16 09:28 copy.of.cp1 -rwxr-xr-x 1 sparre sparre 41061 2007-01-16 09:25 cp1 %
When we want to read from a file, the most efficient way of accessing
it, is to ask the operating system to pretend that the file is actually a
part of the memory allocated to our program. We do that using one of the
What makes this much more efficient is first of all that we leave the job of copying data to and from disk to the operating system. This is something the operating system controls anyway, so we can just as well leave all the control to it.
The second thing that makes this much more efficient is that we don't have to make as many system calls. The reading of the UTMP data has been cut down to four system calls. In the other versions there are two system calls plus one system call per entry in the UTMP file (here on my laptop this adds up to 34 system calls).
It is only the core of
procedure Who we
have to change. The
Show procedures are just the same.
We need an object to handle the access to the UTMP file
UTMP_File, we need to store the size of the file as we map it
into memory (
UTMP_File_Size) and we need the address of the
file data in our memory space (
UTMP_File : File_Descriptor; UTMP_File_Size : System.Storage_Elements.Storage_Offset; UTMP_Memory : System.Address;
We open the UTML file, read its size, and ask to have it mapped into our process memory:
begin UTMP_File := Open (Name => To_POSIX_String (POSIX.UTMP.File_Name), Mode => Read_Only); UTMP_File_Size := Storage_Offset (File_Size (UTMP_File)); UTMP_Memory := Map_Memory (Length => UTMP_File_Size, Protection => Allow_Read, Mapping => Map_Shared, File => UTMP_File, Offset => 0);
Now we pretend to create an array of UTMP records, and locate it at the same place in memory as the UTMP file:
declare Bit_Count : constant Natural := Natural (UTMP_File_Size) * Storage_Element'Size; Record_Count : constant Natural := Bit_Count / POSIX.UTMP.Instance'Size; Records : array (1 .. Record_Count) of POSIX.UTMP.Instance; for Records'Address use UTMP_Memory;
Finally we loop through the array, and show the records in it:
begin for Index in Records'Range loop Show (Item => Records (Index)); end loop; end; Unmap_Memory (First => UTMP_Memory, Length => UTMP_File_Size); Close (File => UTMP_File); end Who;
The complete source code is available as
who4.adb. Please build and test it. How does it
compare with the system
Compile and test this version:
% gnatmake -P who4 [...] % ./who4 christin :0 Feb 9 18:21 sparre :20 Feb 9 19:00 sparre pts/1 Feb 21 13:17 (:20.0) sparre pts/0 Feb 21 12:59 (:20.0) sparre pts/2 Feb 21 13:12 (:20.0) sparre pts/7 Feb 21 09:05 (:20.0) sparre pts/8 Feb 21 09:05 (:20.0) sparre pts/6 Feb 21 09:05 (:20.0) % who christina :0 2007-02-09 18:21 sparre :20 2007-02-09 19:00 sparre pts/1 2007-02-21 13:17 (:20.0) sparre pts/0 2007-02-21 12:59 (:20.0) sparre pts/2 2007-02-21 13:12 (:20.0) sparre pts/7 2007-02-21 09:05 (:20.0) sparre pts/8 2007-02-21 09:05 (:20.0) sparre pts/6 2007-02-21 09:05 (:20.0) % % LANG=C who christina :0 Feb 9 18:21 sparre :20 Feb 9 19:00 sparre pts/1 Feb 21 13:17 (:20.0) sparre pts/0 Feb 21 12:59 (:20.0) sparre pts/2 Feb 21 13:12 (:20.0) sparre pts/7 Feb 21 09:05 (:20.0) sparre pts/8 Feb 21 09:05 (:20.0) sparre pts/6 Feb 21 09:05 (:20.0) %
utmp_buffer.adb are included for direct
comparison with the examples in the book. Please remember that buffered I/O
is generally less efficient than memory mapping of
package POSIX.IO is [...] type Position is (From_Beginning, From_Current_Position, From_End_Of_File); procedure Seek (File : in File_Descriptor; Offset : in IO_Offset; Result : out IO_Offset; Starting_Point : in Position := From_Beginning); [...] end POSIX.IO;
POSIX.IO.Seek allows you to set the current
position to locations after the end of the file. [...]
who4.adb as a basis for your
who am i implementation.
If you want to implement
whoami, you may want to look at
the specifications for
tailcommand, page 70
Consider using the functions
Ada.Strings.Fixed.Index for this problem.
Compare the speed of your implementation of
tail with that
of the system version.
Go to next chapter.
Understanding Unix/Linux Programming, Bruce Molay, ISBN 0-13-008396-8.