private use, such as a custom malloc() implementation. MAP_FIXED causes the kernel to place the map at a specific address. If the address is already in use or otherwise unavail- able, mmap() fails. If MAP_FIXED is not specified and address is unavailable, the kernel will attempt to place the region elsewhere in memory. MAP_LOCKED allows processes with root privilege to lock the region into memory so it will never be swapped to disk. User space programs cannot use MAP_LOCKED, a security feature that prevents unauthorized processes from locking all available memory, which would essentially bring the system to a standstill. Using the munmap() Function When you have finished using a memory mapped file, call munmap() to unmap the region and return the memory to the operating system. This function uses the following proto- type: int munmap(void *start, size_t length); The start argument points to the beginning of the region to unmap, and length indi- cates how much of the region to unmap. After a memory block has been unmapped, fur- ther attempts to access start will cause a segmentation fault (generate a SIGSEGV). When a process terminates, all memory maps are unmapped. The munmap() function returns 0 on success or, on failure, -1 and sets errno. Using the msync() Function The msync() function writes a mapped file to disk. It uses the following prototype: int msync(const void *start, size_t length, int flags); Call msync() to update the disk file with changes made to the in-core map. The region to flush to disk begins at the start address; length bytes will be flushed. The flags argu- ment is a bitwise OR of one or more of the following: MS_ASYNC Schedules a write and returns MS_SYNC Data are written before msync() returns MS_INVALIDATE Invalidate other maps of the same file so they will be updated with new data Using the mprotect() Function The mprotect() function modifies the protection on a memory map. This function uses the following prototype: int protect(const void *addr, size_t len, int prot); System Programming P ART II 254 1772316072 CH14 7/26/99 2:32 PM Page 254 This function call modifies the protections on the memory region that begins at addr to the protections specified in prot, a bitwise OR of one or more of the flags listed in Table 14.1. It returns zero on success or, on failure, -1 and sets errno. Locking Memory Without going into the nitty-gritty details of how it works, memory locking means pre- venting a memory area from being swapped to disk. In a multitasking, multiuser system such as Linux, areas of system memory (RAM) not in active use may be temporarily written to disk (swapped out) in order for that memory to be put to other uses. Locking the memory sets a flag that prevents it from being swapped out. There are four functions for locking and unlocking memory: mlock(), mlockall(), munlock(), and munlockall(). Their prototypes are listed below. int mlock(const void *addr, size_t len); int munlock(void *addr, size_t len); int mlockall(int flags); int munlockall(void); The memory region to be locked or unlocked is specified in addr and len indicates how much of the region to lock or unlock. Values for flags may be one or both of MCL_CUR- RENT, which requests that all pages are locked before the call returns, or MCL_FUTURE, indicating that all pages added to the process’ address space should be locked. As noted in the discussion of mmap(), only processes with root privilege may lock or unlock mem- ory regions. Using the mremap() Function Use the mremap() function to change the size of a mapped file. This function uses the following prototype: void *mremap(void *old_addr, size_t old_len, ➥size_t new_len, unsigned long flags); You will occasionally need to resize a memory region, which is the reason for this func- tion. An analogue of the realloc() call discussed earlier, mremap() resizes the memory region beginning at old_addr, originally with size old_len, to new_len. flags indicates whether the region can be moved in memory if necessary. MREMAP_MAYMOVE permits the address to change; if not specified, the resize operation fails. mremap() returns the address of the resized region or NULL on failure. Memory Management C HAPTER 14 255 14 MEMORY MANAGEMENT 1772316072 CH14 7/26/99 2:32 PM Page 255 Implementing cat(1) Using Memory Maps Listing 14.2 illustrates using memory mapped files. Although it is a naive cat(1) imple- mentation, it clearly demonstrates using memory mapped files. LISTING 14.2 A cat(1) IMPLEMENTATION USING MEMORY MAPS 1 /* 2 * Listing 14.2 3 * mmcat.c - Implement the cat(1) command using mmap() and family 4 */ 5 #include <sys/types.h> 6 #include <sys/mman.h> 7 #include <sys/stat.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 #include <stdlib.h> 11 #include <stdio.h> 12 13 void err_quit(char *msg); 14 15 int main(int argc, char *argv[]) 16 { 17 int fdin; 18 char *src; 19 struct stat statbuf; 20 off_t len; 21 22 /* open the input file and stdout */ 23 if(argc != 2) 24 err_quit(“usage: mmcat <file>”); 25 26 if((fdin = open(argv[1], O_RDONLY)) < 0) 27 err_quit(“open failed”); 28 29 /* need the size of the input file for mmap() call */ 30 if((fstat(fdin, &statbuf)) < 0) 31 err_quit(“fstat failed”); 32 len = statbuf.st_size; 33 34 /* map the input file */ 35 if((src = mmap(0, len, PROT_READ, MAP_SHARED, ➥fdin, 0)) == (void *)-1) 36 err_quit(“mmap failed”); 37 38 /* write it out */ 39 fprintf(stdout, “%s”, src); 40 41 /* clean up */ 42 close(fdin); System Programming P ART II 256 1772316072 CH14 7/26/99 2:32 PM Page 256 43 munmap(src, len); 44 45 exit(0); 46 } 47 48 void err_quit(char *msg) 49 { 50 perror(msg); 51 exit(EXIT_FAILURE); 52} The interesting pieces of code in this program are lines 30–39. As the comment indi- cates, we need the input file’s size for the mmap() call, hence the call to fstat() on line 30 (fstat() and open() (line 26) are discussed in Chapter 9, “I/O Routines,” and Chapter 10, “File Manipulation,” respectively). Once we have the file mapped into mem- ory (line 35), we can use the pointer, src, exactly as if we had populated it with an fread() or fgets() call, as we do in the fprintf() statement on line 39. We return to the memory region to the kernel on line 43 by calling munmap(). perror(), used in the utility function err_quit(), was introduced in Chapter 13, “Handling Errors.” From a practical point of view, using a memory mapped file in this example was overkill because it buys us little in terms of performance or code length. In situations where per- formance is crucial or when you are dealing with time-sensitive operations, however, memory mapped files can be a definite plus. Memory mapping can also be valuable in high security situations. Because processes running with root privilege can lock memory mapped files into memory, preventing them from being swapped to disk by Linux’s memory manager, sensitive data such as password files will be less susceptible to scanner programs. Of course, in such a situation, the memory region would have to be set to PROT_NONE so that other process cannot read the region. Now that you know more about memory maps, the next section examines a few tools to help you debug memory problems. Finding and Fixing Memory Problems This section covers a few tools that help you locate memory management problems in your code. Because C assumes you know what you are doing, most C compilers ignore uses of uninitialized memory, buffer overruns, and buffer underruns. Nor do most com- pilers catch memory leaks or dangling pointers. The tools discussed in this section make up for these compiler shortcomings. Memory Management C HAPTER 14 257 14 MEMORY MANAGEMENT 1772316072 CH14 7/26/99 2:32 PM Page 257 A Problem Child Listing 14.3 is beset with bugs, including the following: • A memory leak (line 18) • Overruns the end of dynamically allocated heap memory (lines 22 and 28) • Underruns a memory buffer (line 32) • Frees the same buffer twice (lines 36 and37) • Accesses freed memory (lines 40 and41) • Clobbers statically allocated stack and global memory (lines 48 and 44, respectively) LISTING 14.3 A PROGRAM WITH MEMORY BUGS 1 /* 2 * Listing 14.3 3 * badmem.c - Demonstrate usage of memory debugging tools 4 */ 5 #include <stdlib.h> 6 #include <stdio.h> 7 #include <string.h> 8 9 char g_buf[5]; 10 11 int main(void) 12 { 13 char *buf; 14 char *leak; 15 char l_buf[5]; 16 17 /* Won’t free this */ 18 leak = malloc(10); 19 System Programming P ART II 258 NOTE Actually, most compilers accept various switches and options that enable them to catch some subset of the errors just mentioned. gcc, for example, has the - Wall option (discussed in Chapter 3, “GNU cc”). In general, however, compilers do not detect all memory problems, making the tools covered in this section quite valuable. 1772316072 CH14 7/26/99 2:32 PM Page 258 20 /* Overrun buf a little bit */ 21 buf = malloc(5); 22 strcpy(buf, “abcde”); 23 fprintf(stdout, “LITTLE : %s\n”, buf); 24 free(buf); 25 26 /* Overrun buf a lot */ 27 buf = malloc(5); 28 strcpy(buf, “abcdefgh”); 29 fprintf(stdout, “BIG : %s\n”, buf); 30 31 /* Underrun buf */ 32 *(buf - 2) = ‘\0’; 33 fprintf(stdout, “UNDERRUN: %s\n”, buf); 34 35 /* free buf twice */ 36 free(buf); 37 free(buf); 38 39 /* access free()ed memory */ 40 strcpy(buf, “This will blow up”); 41 fprintf(stdout, “FREED : %s\n”, buf); 42 43 /* Trash the global variable */ 44 strcpy(g_buf, “global boom”); 45 fprintf(stdout, “GLOBAL : %s\n”, g_buf); 46 47 /* Trash the local variable */ 48 strcpy(l_buf, “local boom”); 49 fprintf(stdout, “LOCAL : %s\n”, l_buf); 50 51 exit(0); 52} None of these bugs, however, prevent the program from executing, but leaks and clob- bered memory usually show up as unpredictable behavior elsewhere in the program. On my system, the program’s output was: $ ./badmem LITTLE : abcde BIG : abcdefgh UNDERRUN: abcdefgh FREED : This will blow up GLOBAL : global boom LOCAL : local boom On other systems, especially those configured to allow core dumps, the sample program may dump core on the second call to free() (line 37). Memory Management C HAPTER 14 259 14 MEMORY MANAGEMENT 1772316072 CH14 7/26/99 2:32 PM Page 259 Using mpr and check to Locate Memory Problems The first tool covered here is Taj Khattra’s mpr package, available from your favorite Metalab mirror (ftp://metalab.unc.edu/pub/Linux/devel/lang/c/mpr-1.9.tar.gz). It can be used to find memory leaks, but it does not find memory corruption errors. In addition, mpr also generates allocation statistics and patterns, but those features will not be covered in this section. mpr’s method uses simple brute force: it logs all allocation and free requests to an external log file that is later processed using mpr’s utilities. To use mpr, download and compile it. The package includes several utility programs and a static library, libmpr.a, that you link your program against. To compile Listing 14.3, the command line was: $ gcc -g badmem.c -o badmem -lmpr -L $HOME/lib Be sure to use the -g switch to generate debugging symbols because some of mpr’s pro- grams require them. Recall from Chapter 3, “GNU cc,” that -lmpr links badmem against libmpr.a, and -L $HOME/lib prepends $HOME/lib to the library search path. Once the program is compiled and linked, set the environment variables $MPRPC and $MPRFI. mpr uses $MPRPC to traverse and display the call chain for each allocation and free request, while $MPRFI defines a pipeline command for logging (and, optionally, filtering) mpr’s output. $ export MPRPC=`mprpc badmem` $ export MPRFI=”cat > badmem.log” With these preliminary steps out of the way, execute the program. If all goes as planned, you should wind up with a file named badmem.log in the current directory. It will look something like the following: m:134522506:134516229:134514813:10:134561792 m:134522506:134516229:134514826:5:134565888 f:134522614:134520869:134514880:134565888 m:134522506:134516229:134514890:5:134565888 f:134522614:134520869:134514975:134565888 f:134522614:134520869:134514987:134565888 This isn’t very informative as is, but the mpr documentation explains the format. The log file provides the raw material for mpr’s utility programs, which parse, slice, dice, and julienne the log to create meaningful information. System Programming P ART II 260 1772316072 CH14 7/26/99 2:32 PM Page 260 To view memory leaks, use mprlk: $ mprlk < badmem.log | mpr -f -l badmem or $ mpr -f -l badmem < badmem.log | mprlk The -f and -l options report the filename and line number where mpr detects the leak. In either case, the output is mprlk: f:main(badmem.c,37):134565888 (NR=6) m:main(badmem.c,18):10:134561792 The output indicates that on line 18 of badmem.c, in the main() function, we malloc() 10 bytes of memory that we never free (the long decimal number is the call chain counter, which mpr and its utilities use precisely to track each allocation and free request). Looking back at Listing 14.3, this is exactly correct. I mentioned a moment ago that mpr cannot detect memory corruption errors. While this is true, mpr includes the mcheck() function from GNU’s malloc() library, which enables you to detect buffer overruns, buffer underruns, and multiple free()s of the same block. In fact, mpr compiles mcheck() into libmpr.a by default. So, the good news is that the buffer overruns and underruns in Listing 14.3 will cause it to abort unless you specifical- ly instruct mpr not to use mcheck(). The bad news is that mcheck() is not terribly infor- mative—it merely complains about a problem and leaves the programmer to determine where the problem occurs. Compiled with mcheck(), the sample program aborts each time we clobber memory: $ ./badmem LITTLE : abcde mcheck: memory clobbered past end of allocated block IOT trap/Abort After fixing the first overrun, the program gets a little farther: $ ./badmem LITTLE : abcde BIG : abcdefgh UNDERRUN: abcdefgh mcheck: memory clobbered before allocated block IOT trap/Abort Memory Management C HAPTER 14 261 14 MEMORY MANAGEMENT 1772316072 CH14 7/26/99 2:32 PM Page 261 Interestingly, mcheck() ignores the larger overrun on line 28, but dies, as you would expect, when the program underruns the buffer on line 32. After fixing these two errors, mcheck() complains about freeing memory twice, as shown in the following code: $ ./badmem LITTLE : abcde BIG : abcdefgh UNDERRUN: mcheck: block freed twice IOT trap/Abort Fixing the other errors is left as an exercise for you. Electric Fence The next tool covered is Electric Fence, written by Bruce Perens. Electric Fence does not catch memory leaks, but it does an excellent job of detecting buffer overruns. You can obtain it from ftp://metalab.unc.edu/pub/Linux/devel/lang/c, although many Linux distributions also ship with it. Electric Fence uses a system’s virtual memory hardware to detect illegal memory access- es, stopping a program on the first instruction that causes a boundary violation. It accom- plishes this by replacing the normal malloc() with its own malloc(), and allocating a small section of memory after the requested allocation that the process is not permitted to access. As a result, buffer overruns cause a memory access violation, which aborts the program with a SIGSEGV (segmentation violation). If your system is configured to allow core files (execute ulimit -c to get and set the size of core files allowed), you can then use a debugger to isolate the location of the overrun. Like mpr, to use Electric Fence you have to link your program against a special library, libefence.a: $ gcc -ggdb badmem.c -o badmem -lefence The compile command used the -ggdb option to generate extra gdb-compatible debug- ging symbols. When executed, the program aborts and dumps core: $ ./badmem Electric Fence 2.0.5 Copyright © 1987-1995 Bruce Perens. LITTLE : abcde Segmentation fault (core dumped) Next, using the core file, run badmem from the gdb debugger (just follow the example for the time being, because gdb is covered in detail in Chapter 36, “Debugging: GNU gdb”). $ gdb badmem GNU gdb 4.17 System Programming P ART II 262 1772316072 CH14 7/26/99 2:32 PM Page 262 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions . Type “show copying” to see the conditions. There is absolutely no warranty for GDB. Type “show warranty” for details. This GDB was configured as “i386-COL-linux” (gdb) run Starting program: /home/kwall/projects/unleashed/src/14/badmem Electric Fence 2.0.5 Copyright (C) 1987-1995 Bruce Perens. LITTLE : abcde Program received signal SIGSEGV, Segmentation fault. strcpy (dest=0x40003ff8 “abcdefgh”, src=0x8055e0c “abcdefgh”) at strcpy.c:35 strcpy.c:35: No such file or directory. (gdb) where #0 strcpy (dest=0x40003ff8 “abcdefgh”, src=0x8055e0c “abcdefgh”) at strcpy.c:35 #1 0x80481be in main () at badmem.c:28 #2 0x80480ee in ___crt_dummy__ () (gdb) The second line from the bottom of the listing makes it crystal clear that there is a prob- lem at line 28 in badmem.c in the main() function. Once you fix this problem, you would then recompile and rerun the program, and, if it aborts again, repeat the debug/fix/recom- pile sequence. Once you’ve thoroughly debugged all of your code, recompile without linking against Electric Fence, and you should be set. But wait, Electric Fence caught the big overrun on line 28, but it missed the little overrun on line 22. How could this be? This peculiar behavior results from the way the CPU aligns allocated memory. Most modern CPUs require that memory blocks be aligned on their natural word size. Intel x86 CPUs, for example, require that memory regions begin at addresses evenly divisible by four, so malloc() calls ordinarily return pieces of memory aligned accordingly. Electric Fence does the same. So, a request for five bytes actually results in eight bytes being allocated in order to meet the memory alignment requirements! As a result, the small buffer overrun on line 22 slips through the fence. Fortunately, Electric Fence allows you to control its alignment behavior using the envi- ronment variable $EF_ALIGNMENT. Its default value is sizeof(int), but if you set it to zero (0), Electric Fence will detect smaller overruns. After setting $EF_ALIGNMENT to 0, recompiling, and rerunning the program, Electric Fence catches the small overrun at line 22: Memory Management C HAPTER 14 263 14 MEMORY MANAGEMENT 1772316072 CH14 7/26/99 2:32 PM Page 263 [...]...1772316072 CH 14 7/26/99 2:32 PM Page 2 64 2 64 System Programming PART II Program received signal SIGSEGV, Segmentation fault strcpy (dest=0x40003ffb “abcde”, src=0x8055df8 “abcde”) at strcpy.c:35 strcpy.c:35: No such file or directory (gdb) where #0 strcpy (dest=0x40003ffb “abcde”, src=0x8055df8 “abcde”) at ➥strcpy.c:35 #1 0x8 048 17e in main () at badmem.c:22 #2 0x8 048 0ee in _crt_dumhttp://www.sds.lcs.mit.edu/lclint/my... Interprocess Communication and Network Programming PART III first two bytes: 10 15 Test iteration 6 first two bytes: 10 15 first two bytes: 12 18 Test iteration 7 first two bytes: 12 18 first two bytes: 14 21 Test iteration 8 first two bytes: 14 21 first two bytes: 16 24 Test iteration 9 first two bytes: 16 24 first two bytes: 18 27 colossus:/home/markw/MyDocs/LinuxBook/src/IPC/SHARED # There are two... see the following output: markw@colossus:/home/markw/MyDocs/LinuxBook/src/IPC/MESSAGEQ > make cc -o message_q message_q.c markw@colossus:/home/markw/MyDocs/LinuxBook/src/IPC/MESSAGEQ > message_q unique key=162757 046 1 message queue id=129 Sending message message of type 123 received with data: test message markw@colossus:/home/markw/MyDocs/LinuxBook/src/IPC/MESSAGEQ > The sample program prints out a... development system You will see in this chapter that you need to configure your Linux system to dedicate some real memory for shared memory allocations This dedicated memory cannot be used by Linux or application programs Configuring Linux to Use Shared Memory You will have to allocate a small block of memory when you boot up Linux by modifying your /etc/lilo.config file and rebooting your system Here... /etc/lilo.config file to allow using shared memory: # Linux bootable (use 1 megabyte for shared memory) image = /vmlinuz append=”mem=63m” root = /dev/hda2 label = linux # Linux bootable partition config ends I added the append statement to my lilo.config file to indicate that my system only has 63 megabytes of physical memory My system actually has 64 megabytes of physical memory, so this effectively... markw@colossus:/home/markw/MyDocs/LinuxBook/src/IPC/SHARED > make cc -o shared_mem shared_mem.c markw@colossus:/home/markw/MyDocs/LinuxBook/src/IPC/SHARED > su Password: colossus:/home/markw/MyDocs/LinuxBook/src/IPC/SHARED # /shared_mem Test iteration 0 first two bytes: 0 0 first two bytes: 0 0 Test iteration 1 first two bytes: 0 0 first two bytes: 2 3 Test iteration 2 first two bytes: 2 3 first two bytes: 4 6 Test iteration... bugs: mpr, Electric Fence, and LCLint MEMORY MANAGEMENT Summary 14 1772316072 CH 14 7/26/99 2:32 PM Page 266 266 1872316072 part3 7/26/99 2:12 PM Page 267 Interprocess Communication and Network Programming IN THIS PART • Introduction to IPC: Using Pipes • Message Queues 275 • Shared Memory • Semaphores 269 281 287 • TCP/IP and Socket Programming • UDP: The User Data Protocol • Using Multicast Sockets... correctly 279 2072316072 CH16 7/26/99 2: 04 PM Page 280 280 2172316072 CH17 7/26/99 2:03 PM Page 281 CHAPTER 17 Shared Memory by Mark Watson IN THIS CHAPTER • Configuring Linux to Use Shared Memory 282 • Sample Program Using Shared Memory 283 • Running the Shared Memory Program Example 285 2172316072 CH17 7/26/99 2:03 PM Page 282 282 Interprocess Communication and Network Programming PART III Shared memory... many older UNIX utilities were written using them 2072316072 CH16 7/26/99 2: 04 PM Page 275 CHAPTER 16 Message Queues by Mark Watson IN THIS CHAPTER • Creating a Sample Message Queue Program 276 • Running the Sample Message Queue Program 278 2072316072 CH16 7/26/99 2: 04 PM Page 276 276 Interprocess Communication and Network Programming PART III Message queues are similar to using pipes but have the advantage... between processes that are running on the same computer In Chapter 19, “TCP/IP and Socket Programming, ” socket programming is used to provide a solution for communication between programs running on different computers Any corrections and updates for this chapter can be found at http://www.markwatson.com/books /linux_ prog.html Like pipes, message queues are largely interesting for historical reasons . following: m:1 345 22506:1 345 16229:1 345 148 13:10:1 345 61792 m:1 345 22506:1 345 16229:1 345 148 26:5:1 345 65888 f:1 345 226 14: 1 345 20869:1 345 148 80:1 345 65888 m:1 345 22506:1 345 16229:1 345 148 90:5:1 345 65888 f:1 345 226 14: 1 345 20869:1 345 149 75:1 345 65888 f:1 345 226 14: 1 345 20869:1 345 149 87:1 345 65888 This. /* clean up */ 42 close(fdin); System Programming P ART II 256 1772316072 CH 14 7/26/99 2:32 PM Page 256 43 munmap(src, len); 44 45 exit(0); 46 } 47 48 void err_quit(char *msg) 49 { 50 perror(msg); 51. following: m:1 345 22506:1 345 16229:1 345 148 13:10:1 345 61792 m:1 345 22506:1 345 16229:1 345 148 26:5:1 345 65888 f:1 345 226 14: 1 345 20869:1 345 148 80:1 345 65888 m:1 345 22506:1 345 16229:1 345 148 90:5:1 345 65888 f:1 345 226 14: 1 345 20869:1 345 149 75:1 345 65888 f:1 345 226 14: 1 345 20869:1 345 149 87:1 345 65888 This isn’t very informative as is, but the mpr documentation explains