1 / 30

Standard C Libraries

Standard C Libraries. Application Programmming Interface to System-Calls. Important File-I/O Functions. int open( char *pathname, int flags, … ); int read( int fd, void *buf, size_t count ); int write( int fd, void *buf, size_t count ); int lseek( int fd, loff_t offset, int whence );

hank
Download Presentation

Standard C Libraries

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Standard C Libraries Application Programmming Interface to System-Calls

  2. Important File-I/O Functions int open( char *pathname, int flags, … ); int read( int fd, void *buf, size_t count ); int write( int fd, void *buf, size_t count ); int lseek( int fd, loff_t offset, int whence ); int close( int fd );

  3. UNIX ‘man’ pages • A convenient online guide to prototypes and semantics of the C linrary functions • Example of usage: $ man 2 open

  4. The ‘open’ function • #include <fcntl.h> • int open( char *pathname, int flags, … ); • Converts a pathname to a file-descriptor • File-descriptor is a nonnegative integer • Used as a file-ID in subsequent functions • ‘flags’ is a symbolic constant: O_RDONLY, O_WRONLY, O_RDWR

  5. The ‘close’ function • #include <unistd.h> • int close( int fd ); • Breaks link between file and file-descriptor • Returns 0 on success, or -1 if an error

  6. The ‘write’ function • #include <unistd.h> • int write( int fd, void *buf, size_t count ); • Attempts to write up to ‘count’ bytes • Bytes are taken from ‘buf’ memory-buffer • Returns the number of bytes written • Or returns -1 if some error occurred • Return-value 0 means no data was written

  7. The ‘read’ function • #include <unistd.h> • int read( int fd, void *buf, size_t count ); • Attempts to read up to ‘count’ bytes • Bytes are placed in ‘buf’ memory-buffer • Returns the number of bytes read • Or returns -1 if some error occurred • Return-value 0 means ‘end-of-file’

  8. Notes on ‘read()’ and ‘write()’ • These functions have (as a “side-effect”) the advancement of a file-pointer variable • They return a negative function-value of -1 if an error occurs, indicating that no actual data could be transferred; otherwise, they return the number of bytes read or written • The ‘read()’ function normally does not return 0, unless ‘end-of-file’ is reached

  9. The ‘lseek’ function • #include <unistd.h> • off_t lseek( int fd, off_t offset, int whence ); • Modifies the file-pointer variable, based on the value of whence: enum { SEEK_SET, SEEK_CUR, SEEK_END }; • Returns the new value of the file-pointer (or returns -1 if any error occurred)

  10. Getting the size of a file • For normal files, your application can find out how many bytes belong to a file using the ‘lseek()’ function: int filesize = lseek( fd, 0, SEEK_END ); • But afterward you need to ‘rewind’ the file if you want to read its data: lseek( fd, 0, SEEK_SET );

  11. What about ‘pseudo’ files? • You can use standard library functions to open, read, and close a ‘/proc’ pseudo-file • You can use ‘lseek’ (except SEEK_END) • An example is our ‘howfast.cpp’ program • It measures how fast ‘jiffies’ increments • It opens, reads, and closes ‘/proc/jiffies’ • And it also uses ‘lseek’ (to rewind this file)

  12. How these system-calls work Operating System Kernel C Runtime Library Application Program Module ‘methods’ User-space Kernel-space

  13. Special ‘device’ files • UNIX systems treat hardware-devices as special files, so that familiar functions can be used by application programmers to access these devices (open, read, close) • But a System Administrator has to create these device-files (in the ‘/dev’ directory) • There are two categories of device files: ‘character’ devices, and ‘block’ devices

  14. The ‘mknod’ command • To create the device-node for a character device, an Administrator executes ‘mknod’ root# mknod /dev/scull c 48 0 • Here ‘/dev/scull’ is the file’s pathname, ‘c’ indicates that it’s a character-mode device, 48 is its (unique) ‘major’ ID-number, and 0 is its (unique) ‘minor’ ID-number • Default access-privileges: r w - r - - r - - • Can be modified using ‘chmod’ command

  15. What’s new in 2.6? • Earlier Linux kernels stored the ‘/dev’ files on the hard disk (so they were persistent) • The 2.6 kernel stores them in a ram-disk • So they ‘disappear’ during every shutdown • You need ‘root’ privileges to re-build them! • (Fortunately this step can be automated if device-nodes are in ‘/etc/udev/devices’ )

  16. A useful device-driver • We can create a character-mode driver for the processor’s physical memory (i.e. ram) • (Our machines have 1-GB of physical ram) • But another device-file is named ‘/dev/ram’ so ours will be: ‘/dev/dram’ (dynamic ram) • We’ve picked 253 as its ‘major’ ID-number • Our SysAdmin setup a device-node using: root# mknod /dev/dram c 253 0

  17. Device knowledge • Before you can write a device-driver, you must understand how the hardware works • Usually this means you need to obtain the programmer manual (from manufacturer) • Nowdays this can often be an obstacle • But some equipment is standardized, or is well understood (because of its simplicity)

  18. 1-GB RAM has ‘zones’ ZONE_NORMAL ZONE_HIGH 128-MB 1024-MB (= 1GB) 16-MB ZONE_LOW

  19. Legacy DMA • Various older devices rely on the PC/AT’s DMA controller to perform data-transfers • This chip could only use 24-bit addresses • Only the lowest 16-megabytes of physical memory are ‘visible’ to these devices: 224 = 0x01000000 (16-megabytes) • Linux tries to conserve its use of memory from this ZONE_LOW region for anything except DMA (so it will available if needed)

  20. ‘HIGH’ memory • Linux traditionally tried to ‘map’ as much physical memory as possible into virtual addresses allocated to the kernel-space • Before the days when systems had 1-GB or more of installed memory, Linux could linearly map ALL of the physical memory into the 1-GB kernel-region: 0xC0000000 – 0xFFFFFFFF • But with 1GB there’s not enough room!

  21. The 896-MB limit not-mapped DRAM (1GB) HIGH MEMORY User space (3GB) Kernel space (1GB) 896-MB linearly mapped Physical address-space A special pair of kernel-functions named ‘kmap()’ and ‘kunmap()’ can be called by device-drivers to temporarily map pages of physical memory into ‘vacant’ areas within the kernel’s virtual address-space Virtual address-space

  22. ‘copy_to_user()’ • With kernel 2.6, it is possible to configure the user-space versus kernel-space ‘split’ so that nearly 4GB of physical memory is always linearly mapped into kernel-space • The configuration-option is CONFIG_4GB • With this option enabled, the user-space and kernel-space use two different maps • So device-drivers need a special function to transfer kernel-data to a user’s buffer

  23. Driver-module structure • We will need three kernel header-files: • #include <linux/module> // for printk(), register_chrdev(), unregister_chrdev() • #include <linux/highmem.h> // for kmap(), kunmap(), and ‘num_physpages’ • #include <asm/uaccess.h> // for copy_to_user()

  24. Our ‘dram_size’ global • Our ‘init_module()’ function will compute the size of the installed physical memory • It will be stored in a global variable, so it can be accessed by our driver ‘methods’ • It is computed from a kernel global using the PAGE_SIZE constant (=4096 for x86) dram_size = num_physpages * PAGE_SIZE

  25. ‘major’ ID-number • Our ‘major’ device ID-number is needed when we ‘register’ our device-driver with the kernel (during initialization) and later when we ‘unregister’ our device-driver (during the cleanup procedure): int my_major = 253; // static ID-assignment

  26. Our ‘file_operations’ • Our ‘dram’ device-driver does not need to implement special ‘methods’ for doing the ‘open()’, ‘write()’, or ‘release()’ operations (the kernel ‘default’ operations will suffice) • But we DO need to implement ‘read()’ and ‘llseek()’ methods • Our ‘llseek()’ code here is very standard • But ‘read()’ is specially crafted for DRAM

  27. Using our driver • We have provided a development tool on the class website (named ‘fileview.cpp’) which can be used to display the contents of files (including device-files) • The data is shown in hex and ascii formats • The arrow-keys can be used for navigation • The enter-key allows an offset to be typed • Keys ‘b’, ‘w’, ‘d’ and ‘q’ adjust data-widths

  28. In-class exercise #1 • Install the ‘dram.ko’ device-driver module; then use ‘fileview’ to browse the contents of the processor’s physical memory: $ fileview /dev/dram

  29. Control Register CR3 • Register CR3 holds the physical-address of the system’s current ‘page-directory’ • The page-directory is an array of 1024 entries, showing how ‘virtual addresses’ are currently ‘mapped’ to physical pages • With ‘fileview’ you can find and examine this important kernel data-structure – but you must know the value in register CR3

  30. In-class exercise #2 • Use the ‘newinfo’ wizard to quickly create a pseudo-file (named ‘/proc/cr3’) that will allow user-programs to obtain the current value of the Pentium’s CR3 register • Write a tool (named ‘seepgdir.cpp’) that will read ‘/proc/cr3’ to get the address of the page-directory, then read it from the ‘/dev/dram’ device and print it onscreen

More Related