Simulations, games, encryption, and statistical analyses all need random numbers. But just how random is your random number generator? DieHarder can help.
cmake is a portable build system: Create a single source definition and build your code on one or many platforms. Learn how to use cmake and see how KDE uses the tool for the project’s next generation build system.
Linus Torvalds once said, "If you deny the Index, you really deny git itself." (February 4, 2006, Git List Archives). Rather than try to sweep the mysteries and complexities of the git Index under the rug, some explanation and examples can help clarify it, expose its power, and allow you to revel in it!
Kernel 2.6 is finally here, and it touts several enhancements over the 2.4 series. The press has highlighted changes relevant to systems architects and managers, but there's plenty in 2.6 for application developers, too.
April's "API Spy" introduced Python's C API and showed how a Python interpreter can be embedded in a C program. For many tasks where you need to run a Python script from within C code, last month's technique is sufficient. However, as your C programs and Python scripts evolve, you may want or need more advanced interaction between the two languages.
If you've worked with Linux for some time, you've probably used a set-user ID (or setuid) program to temporarily gain permissions different from your normal access rights. Unlike typical programs that run with your permissions, a setuid program runs with the permissions of that program's owner. For example, if you launch a program that's setuid and owned by root, that program runs as though root had executed it, temporarily granting you the same (full) access privileges as the superuser.
Many applications, including most of those shipped with Windows, Linux, KDE, Gnome, and Apache are written in C. C is perhaps the most universal of all programming languages -- its expressive power, portability, minimalism, and speed make it a popular choice.
Unix is traditionally very text-oriented: configuration files are plain ASCII, commands are issued via the shell, tools provide feedback via stdout, and daemons and other system services record status in logs.
If you're running RedHat or a Linux distribution based on RedHat, chances are you've had occassion to use RPMs. RPMs and Red Hat's accompanying package management system (and other systems like it) greatly simplify the task of maintaining the software on a system. With RPMs, installs, upgrades, and even downgrades are quick and easy.
In this third and final installment on introductory Qt programming, let's take a look at two graphical development tools that are part of the Qt framework: the GUI editor Qt Designer and the foreign language translation tool Qt Linguist.
In the previous installment of this series, we implemented two very simple example programs, which nevertheless demonstrated quite a few of the core concepts of Qt programming. This month, let's will take a step back and look at some of the fundamentals of programming with Qt.
Qt is a set of C++ libraries and development tools commercially developed and distributed by Trolltech of Norway. Qt is available for different platforms (Win32, most flavors of Unix/Linux, as well as Mac OS X), and in several editions, including the Free Edition that's free of charge for the development of free and open source software. Trolltech's Free Edition has all of the same features of its Enterprise Edition -- at no cost!
Suppose that you want to contact a company that you've found on the Internet, but you don't know where it's located, and the only contact information provided on the "About the Company" web page contains a phone number with the unfamiliar area code of 323. You could call them up, hope to reach a real person, and ask where the company is located. Or, you could look up what geographical region uses that area code. Using everyone's favorite search engine, and within a few clicks, you see that 323 is a new area code for Los Angeles. Problem solved, and time to move on.
The Qt (sometimes pronounced "cute") C++ toolkit has been available since 1995, but it entered the mainstream of the computer programming world just a few years ago. Known for its first-class support of Unix operating systems, Qt, from Norwegian company Trolltech, surged in popularity along with the growth of Linux. Developers have turned to the toolkit for its cross-platform features alone: Qt helps you build Macintosh and Windows programs directly from your Linux source code. Qt also received a boost from the ever-popular KDE, which relies heavily on the toolkit to simplify GUI programming. Even the Linux kernel now requires Trolltech's libraries for its xconfig build module. As a result, Qt has cemented a spot among the best-selling C++ toolkits on the market.
When building someone else's software, there's nothing more demoralizing than seeing whole rafts of compiler warnings fly by. A common reaction to such spewage is, "Are these real issues requiring investigation, or is this just the handiwork of a sloppy developer?" Not all warnings indicate problems with code, but there's no way to know for certain without actually looking at each and every warning. Needless to say, when warnings turn out to be nothing, it's downright frustrating -- and a huge waste of time. So, professional developers not only work hard to eliminate compiler warnings, they ask the compiler to produce more of them. Why? There are three very good reasons:
Before releasing any amount of code, developers usually test their work to tune performance and prove that the software works as intended. But often, validation is quite difficult, even if the application is simple.
Dynamic memory allocation seems straightforward enough: you allocate memory on demand -- using malloc() or one of its variants -- and free memory when it's no longer needed. Indeed, memory management would be that easy -- if only we programmers never made mistakes. Alas, we do make mistakes (from time to time) and memory management problems do occur.
Many C and C++ applications use a plug-in or modular architecture to add features dynamically. Unlike monolithic applications, where all features are compiled into a single executable, modular applications typically have a central engine and a set of complementary feature libraries. Each library -- usually called a plug-in or a module -- implements a unique feature. When that specific feature is needed, the engine simply loads the module on demand, and calls the module to do the work.
In September, we discussed the significant advantages of re-implementing desired, but less common functions: if you use a feature of your local operating system, but discover it doesn't exist on other platforms, write your own implementation, and make that code a part of your distribution.
Last month, we talked about writing portable code and focused on how to use feature test macros, coding standards, and emulation of uncommon functions to make porting even easier. This month, we'll talk about portable build systems -- tools you can use to easily build that portable code on a variety of target platforms.
Everyone professes to write portable code, but few programmers actually manage to do it. In most cases, so-called portable code comes out littered with #ifs or #ifdefs (or worse, nested #ifs and #ifdefs), rendering the code illegible and obfuscated. Sadly, the lion's share of many porting efforts is spent trying to figure out which lines of code are actually being compiled and executed. This wastes time and energy, and can be downright frustrating.
This month, we finish peeking under the hood of the compiler with a look at the last two steps in the compilation process (the process that turns your source code into something that a machine can actually execute). If you recall, there are seven steps in compilation. These steps are shown in Figure One. Last month, we looked at step five, "Intermediate Code Optimization", those optimizations that the compiler can perform independent of the architecture of the target machine.
Last month, we began investigating how compilers actually work. Our look "under the hood" started with the front end of the compiler -- the phases that parse and tokenize the source file, verify syntax and semantics (the rules of the programming language), and translate the source code into an intermediate representation. Figure One shows a overview of the entire process. This month, we pick up the discussion at step five, "Intermediate Code Optimization".
Over time, this column has discussed many programming topics and techniques. However, one subject we have never fully addressed is what actually happens at "compile time." How does a compiler take the program you write and translate it into something that a machine can understand and execute?
For the past few months, we've been learning how the compiler and linker work together to take the programs you write and convert them into executables that the operating system can run. We've followed the process from source code to object module to executable, with static and shared libraries thrown in as well.
Over the past several months, this column has shown you how to use gcc and g++ language extensions, how to link objects and functions, and how to build executables. We will continue this month with a discussion about a very specific type of object -- a shared (or dynamic) library -- and how to take advantage of it in your programs.
Your program has compiled with no errors. You type its name and watch it run. It seems so simple, but there's a lot that had to happen behind the scenes at the time the program was compiled in order to make it look so easy. For one thing, in order to make it possible for the kernel to properly load and execute your program, the compiler toolchain has to know exactly how the kernel will expect the new process's virtual address space to look. In other words, the toolchain has to be able to build the executable according to specifications that the kernel understands and expects.
These days, interpretive languages, most notably Perl, JavaScript, and Python, have made the barriers to entry for newly-aspiring programmers a lot lower than they once were. Perl, in particular, makes it easy for a newcomer to get his or her feet wet and leave the deeper mysteries that make for industrial-strength, high-performance software for later on. Languages such as C and C++ that typically get compiled all of the way down to real machine code are a different story, however. These languages, designed by professional software engineers for professional software engineers, generally assume that you, the programmer, are able to get down to the gritty details (and often idiosyncratic quirks) of the underlying hardware and the software development tool set you will be using.
Many of you are familiar with the C and C++ languages. You know the syntax and the semantics of the various operations and have a feel for what is allowed by the language according to its specification. However, you may (or may not) be surprised to discover that compilers for these languages deviate from the official specifications.
In the past two months, this column has introduced some of the functions necessary for writing networked programs. We've been throwing around terms like TCP, UDP, IP, and others without any real description of what they mean. This month, you should gain a better understanding of these abbreviations and exactly what is going on when they are used to communicate between machines. With this knowledge, you will be better equipped to determine exactly which protocol is appropriate for your applications -- for instance, why UDP is often used for broadcast-style transmissions, while TCP is used for transactions.
Last month we started a discussion on network programming. However, in the interest of getting through an entire example of a client and a server and how they communicate, we omitted many details. This month, we'll examine our examples more closely to gain more knowledge about network programming. Specifically, we will discuss how to get IP addresses from hostnames and hostnames from IP addresses. We will also take a look at the difference between little-endian and big-endian machines and find out why "endianness" matters in network programming.
This month, we are starting a series on network programming. This area of programming is enormous, not only because of the sheer amount of information that is needed to successfully develop network applications, but also because of the number of applications currently being developed with networking in mind. With network speeds increasing, more and more applications have a "network" version of some sort. For example, Quicken can automatically update your account information from some banks, computer games can be played with other people on the Internet, and so on.
Last month we introduced a few functions that allow your applications to retrieve the current time from Linux. We discussed how one might implement a simple function that causes an application to wait for a specific amount of time before continuing execution. We also looked at the alarm() function, which keeps time for you, and how you might use alarm() instead of the timing functions to allow a program to wait for a specific amount of time without needlessly executing any instructions while waiting.
Every now and then, you'll find that in the midst of an application, you really need to know the time from the system clock. Even more likely, you need to have your application wait for a specific amount of time. Linux's timing functions are relatively straightforward; however, most people overlook them until they need to use one in an application.
Last month we introduced some of the concepts that are involved with memory management. We discussed how to dynamically allocate memory in your applications. We also described the various methods that allocators use to manage free memory. This month we turn our attention to memory allocation at a lower level -- the level of the operating system.
This month we're going to examine a topic that is probably familiar to those of you who have any experience with programming -- dynamic memory allocation. In any programs of significant heft, dynamic memory management is a necessity. You are probably experienced with the standard C functions malloc() and free(), and we'll do a brief recap of those memory allocation and deallocation functions. Following that, we will look at how memory allocation in Linux actually works.
For the last several months in this column, we've been looking at programming with Linux's threads library, pthreads. However, we have taken for granted the work that is actually done under the covers by the pthreads libraries. So this month's Compile Time will dissect Linux pthreads themselves to discover exactly what it is that makes them tick.
Welcome to the second part of our look at programming with threads. In last month's column we talked about the functions that allow you to create and wait on threads. This month we're going to dive deeper into the problems that often arise when using threads to write concurrent programs. Before we begin that however, we'll return to the ticket agent example we looked at last month and discuss the solution to the problem of over-selling of tickets.