2010-08-24

Asynchronous I/O on linux


Introduction

"Asynchronous I/O" essentially refers to the ability of a process to perform input/output on multiple sources at one time. More specifically it's about doing I/O when data is actually available (in the case of input) or when output buffers are no longer full, rather than just performing a read/write operation and blocking as a result. This in itself is not so difficult, but typically there are several channels through which I/O must be performed and the key is to monitor these multiple channels simultaneously.
Consider the case of a web server with multiple clients connected. There is one network (socket) channel and probably also one file channel for each client (the files must be read, and the data must be passed to the client over the network). One problem is, how to determine which client socket to send information to next - since, if we send on a channel whose output buffer is full, we will pointlessly block the process and delay sending of information to other clients needlessly. Another problem is to avoid wasting processor cycles in simply checking whether it is possible to perform I/O - to extend the web server example, if all the output buffers are full, it would be nice if the application could sleep until such time as one of the buffers had some free space again (and be automatically woken at that time).
In general Asynchronous I/O revolves around two functions: The ability to determine that input or output is immediately possible without blocking or that a pending I/O operation has completed. Both cases are examples of asynchronous events, that is, they can happen at any time during program execution, and the process need not actually be waiting for it to happen (though it can do so). The distinction between the two is largely a matter of operating mode (it is the difference between performing a read operation, for example, and being notified when the data is in the application's buffer, compared to simply being notified when the data is available and asking that it be copied to the application's buffer afterwards). Note however that the first case is arguably preferable since it potentially avoids a redundant copy operation (the kernel already knows where the data knows to be, and doesn't necessarily need to read it into its own buffer first).
The problem to be solved is how to recieve asynchronous events in a synchronous manner, so that a program can usefully deal with those events. With the exception of signals, asynchronous events do not cause any immediate execution of code within the application; so, the application must check for these events and deal with them in some way. The various AIO mechanisms discussed later provide ways to do this.
Note that I/O isn't the only thing that can happen asynchronously; unix signals can arrive, mutexes can be acquired/released, file locks can be obtained, sync() calls might complete, etc. All these things are also (at least potentially) asynchronous events that may need to be dealt with. Unfortunately the POSIX world doesn't generally recognize this; for instance there is no asynchronous version of the fctnl(fd, F_SETLKW ...) function.
Edge- versus level-triggered AIO mechanisms
There are several mechanism for dealing with AIO, which I'll discuss later. First, it's important to understand the difference between "edge-triggerd" and "level-triggered" mechanisms.
A level-triggered AIO mechanism provides (when queried) information about which AIO events are still pending. In general, this translates to a set of file descriptors on which reading (or writing) can be performed without blocking.
An edge-triggered mechanism on the other hand provides information about which events have changed status (from non-pending to pending) since the last query.
Level-triggered mechanisms are arguably simpler to use, but in fact edge-triggered mechanisms provider greater flexibility and efficiency in certain circumstances, primarily because they do not require redundant information to be provided to the application (i.e. if the application already knows that an event is pending, it is wasteful to tell it again).
Be cautious when using edge-triggered mechanisms. In particular, it is probably not safe to assume that only a single notification will be provided when a file descriptor status changes (it may be that more data coming into the buffer will generate another notification, for instance, though in a perfect kernel implementation this would not happen).
open() in non-blocking mode
It is possible to open a file (or device) in "non-blocking" mode by using the O_NONBLOCK option in the call to open. You can also set non-blocking mode on an already open file using the fcntl call. Both of these options are documented in the GNU libc documentation.
The result of opening a file in non-blocking mode is that calls to read() and write() will return with an error if they are unable to proceed immediately, ie. if there is no data available to read (yet) or the write buffer is full. This makes it possible to continuously iterate through the interesting file descriptors and check for available input (or check for readiness for output) simply by attempting a read (or write). This technique is called polling and is problematic primarily because it needlessly consumes CPU time - that is, the program never blocks, even when no input or output is possible on any file descriptor.
A more subtle problem with non-blocking I/O is that it generally doesn't work with regular files (this is true on linux, even when files are opened with O_DIRECT; possibly not on other operating systems). That is, opening a regular file in non-blocking mode has no effect for regular files: a read will always actually read some of the file, even if the program blocks in order to do so. In some cases this may not be important, seeing as file I/O is generally fast enough so as to not cause long blocking periods (so long as the file is local and not on a network, or a slow medium). However, it is a general weakness of the technique.
(Note, on the other hand, I'm not necessarily advocating that non-blocking I/O of this kind should actually be possible on regular files. The paradigm itself is flawed in this case; why should data ever be made available to read, for instance, unless there is a definite request for it and somewhere to put it? The non-blocking read itself does not serve as such a request, when considered for what it really is: two separate operations, the first being "check whether data is available" and the second being "read it if so").
As well as causing reads and writes to be non-blocking, The O_NONBLOCK flag also causes the open() call itself to be non-blocking for certain types of device (modems are the primary example in the GNU libc documentation). Unfortunately, there doesn't seem to exist a mechanism by which you can execute an open() call in a truly non-blocking manner for regular files (which again, might be particularly desirable for files on a network). The only solution here is to use threads, one for each simultaneous open() operation.
It's clear that, even if non-blocking I/O were usable with regular files, it would only go part-way to solving the asynchronous I/O problem; it provides a mechanism to poll a file descriptor for data, but no mechanism for asynchronous notification of when data is available. To deal with multiple file descriptors a program would need to poll them in a loop, which is wasteful of processor time. On the other hand, when combined with one of the mechanisms yet to be discussed, non-blocking I/O allows reading or writing of data on a file descriptor which is known to be ready up until such point as no more I/O can be performed without blocking.
It may not be strictly necessary to use non-blocking I/O when combined with a level-triggered AIO mechanism, however it is still recommended in order to avoid accidentally blocking in case you attempt more than a single read or write operation or, dare I say it, a kernel bug causes a spurious event notification.
AIO on Linux
There are several ways to deal with asynchronous events on linux; all of them presently have at least some minor problems, mainly due to limitations in the kernel.
Threading
Signals
The SIGIO signal
select() and poll() (and pselect/ppoll)
epoll()
POSIX asynchronous I/O (AIO)
Threading
The use of multiple threads is in some ways an ideal solution to the problem of asynchronous I/O, as well as asynchronous event handling in general, since it allows events to be dealt with asynchronously and any needed synchronization can be done explicitly (using mutexes and similar mechanisms).
However, for large amounts of concurrent I/O, the use of threads has significant problems for practical application due to the fact that each thread requires a stack (and therefore consumes a certain amount of memory) and the number of threads of in a process may be limited by this and other factors. Thus, it may be impractical to assign one thread to each event of interest.
Threading is presently the only way to deal certain kinds of asynchronous operation (obtaining file locks, for example). It can potentially be combined with other types of asynchronous event handling, to allow asynchronous operations where it is otherwise impossible (file locks etc); be warned, thouhg, that it takes a great deal of care to get this right.
In fact, arguably the biggest argument against using threads is that is hard. Once you have a multi-threaded program, understanding the execution flow becomes much harder, as does debugging; and, it's entirely possibly to get bugs which manifest themselves only rarely, or only on certain machines, under certain processor loads, etc.
Signals
Signals can be sent between unix processes by using kill() as documented in the libc manual, or between threads using pthread_kill(). There are also the so-called "real-time" signal interfaces described here. Most importantly, signals can be sent automatically when certain asynchronous events occur; the details are discussed later - for now it's important to understand how signals need to be handled.
Signal handlers as an asynchronous event notification mechanism work just fine, but because they are truly executed asynchronously there is a limit to what they can usefully do (there are a limited number of C library functions which can be called safely from within a signal handler, for instance). A typical signal handler, therefore, often simply sets a flag which the program tests at prudent times during its normal execution. Alternatively a program can use various functions available to wait for signals. These include:
sleep(), nanosleep()
pause()
sigsuspend()
sigwaitinfo(), sigtimedwait()
These functions are used only for waiting for signals (or in some cases a timeout) and can not be used to wait for other asynchronouse events. Many functions not specifically meant for waiting for signals will however return an error with errno set to EINTR should a signal be handled while they are executing. It is worth reading the Glibc documentation on signals to understand the possible race conditions that can occur from relying on this fact too heavily.
See also the discussion of SIGIO below.
sigwaitinfo() and sigtimedwait are special in the above list in that they (a) avoid possible race conditions if used correctly and (b) return information about a pending signal (and remove it from the signal queue) without actually executing the signal handler.
The SIGIO signal
File descriptors can be set to generate a signal when an I/O readiness event occurs on them - except for those which refer to regular files (which should not be surprising by now). This allows using sleep(), pause() or sigsuspend() to wait for both signals and I/O readiness events, rather than using select()/poll(). The GNU libc documentation has some information on using SIGIO. It tells how you can use the F_SETOWN argument to fcntl() in order to specify which process should recieve the SIGIO signal for a given file descriptor. However, it does not mention that on linux you can also use fcntl() with F_SETSIG to specify an alternative signal, including a realtime signal. Usage is as follows:
fcntl(fd, F_SETSIG, signum);
... where fd is the file descriptor and signum is the signal number you want to use. Setting signum to 0 restores the default behaviour (send SIGIO). Setting it to non-zero has the effect of causing the specified signal to be queued when an I/O readiness event occurs, if the specified signal is a non-realtime signal which is already pending (? I need to check this - didn't I mean if it is a realtime signal?). If the signal cannot be queued a SIGIO is sent in the traditional manner.
This technique cannot be used with regular files.
The IO signal technique is an edge-triggered machanism - A signal is sent when the I/O readiness status changes.
If a signal is successfully queued due to an I/O readiness event, additional signal handler information becomes available to advanced signal handlers (see the link on realtime signals above for more information). Specifically the handler will see si_code (in the siginfo_t structure) with one of the following values:
POLL_IN - data is available
POLL_OUT - output buffers are available (writing will not block)
POLL_MSG - system message available
POLL_ERR - input/output error at device level
POLL_PRI - high priority input available
POLL_HUP - device disconnected
Note these values are not necessarily distinct from other values used by the kernel in sending signals. So it is advisable to use a signal which is used for no other purpose. Assuming that the signal is generated to indicate an I/O event, the following two structure members will be available:
si_band - contains the event bits for the relevant fd, the same as would be seen using poll() (see discussion below)
si_fd - contains the relevant fd.
The IO signal technique, in conjunction with the signal wait functions, can be used to reliably wait on a set of events including both I/O readiness events and other signals. As such, it is already close to a complete solution to the problem, except that it cannot be used for regular files ("buffered asynchronous I/O") - a limitation that it shares with various other techniques yet to be discussed.
Note it is possible to assign different signals to different fd's, up to the point that you run out of signals. There is little to be gained from doing so however (it might lead to less SIGIO-yielding signal buffer overflows, but not by much, seeing as buffers are per-process rather than per-signal. I think).
Note also that SIGIO can itself be selected as the notification signal. This allows the assosicated extra data to be retrieved, however, multiple SIGIO signals will not be queued and there is no way to detect if signals have been lost, so it is necessary to treat each SIGIO as an overflow regardless. It's much better to use a real-time signal. If you do, you potentially have an asynchronous event handling scheme which in some cases may be more efficient than using poll() and perhaps even epoll(), which will soon be discussed.
Turning a signal event into an I/O event
With the I/O signal technique described above it's possible to turn an I/O readiness event on a file descriptor into a signal event; now, it's time to talk about how to do the opposite. This allows signals to be used with various other mechanisms that otherwise wouldn't allow it. Of course you only need to do this if you don't want to resort solely to the I/O signal technique.
First, the old fashioned way. This involves creating a pipe (using the pipe() function) and having the signal handler write to one end of the pipe, thus generating data (and a readiness event) at the other end. For this to work properly, note the following:
Writes to the pipe must be non-blocking. Otherwise, the write buffer may become full and the write operation in the signal handler will block, probably causing the whole program to hang.
You must be prepared to correctly handle the write failing due to the write buffer being full. In general this means you cannot rely on being able to determine which signals have occurred just by reading data from the pipe; you must have some method of handling overflow.
The new way of converting signal events to I/O events is to use the signalfd() function, available from Linux kernel 2.6.22 / GNU libc version 2.8. This system call creates a file descriptor from which signal information (for specified signals) can be read directly.
The only advantage of the old technique is that it is portable, because it doesn't require the Linux-only signalfd() call.
The select() and poll() functions, and variants
The select() function is documented in the libc manual. As noted, a file descriptor for a regular file is considered ready for reading if it's not at end-of-file and is always considered ready for writing (the man page for select in the Linux manpages neglects to mention both these facts). As with non-blocking I/O, select is no solution for regular files (which may be on a network or slow media).
While select() is interruptible by signals, it is not generally possible to use plain select() to wait for both signal and I/O readiness events without causing a race condition (see the discussion of signals above).
The pselect() call (not documented in the GNU libc manual) allows atomically unmasking a signal and performing a select() operation (the signal mask is also restored before pselect returns); this allows waiting for one of either a specific signal or an I/O readiness event. It is possible to achieve the same thing without using pselect() by having the signal handler generate an I/O readiness event that the select() call will notice (for instance by writing a byte to a pipe, thereby making data available on the other end; this requires care - the pipe should be in non-blocking mode, and even then the technique is not stricly portable).
Finally, select (and pselect) aren't particularly good from a performance standpoint because of the way the file descriptor sets are passed in (as a bitmask). The kernel is forced to scan the mask up to the supplied nfds argument in order to check which descriptors the userspace process is actually interested in. The poll() function, not documented in the GNU libc manual, is an alternative to select() which uses a variable sized array to hold the relevant file descriptors instead of a fixed size structure.
#include <sys/poll.h>
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
The structure struct pollfd is defined as:
struct pollfd {
int fd; // the relevant file descriptor
short events; // events we are interested in
short revents; // events which occur will be marked here
};
The events and revents are bitmasks with a combination of any of the following values:
POLLIN - there is data available to be read
POLLPRI - there is urgent data to read
POLLOUT - writing now will not block
If the feature test macros are set for XOpen, the following are also available. Although they have different bit values, the meanings are essentially the same:
POLLRDNORM - data is available to be read
POLLRDBAND - there is urgent data to read
POLLWRNORM - writing now will not block
POLLWRBAND - writing now will not block
Just to be clear on this, when it is possible to write to an fd without blocking, all three of POLLOUT, POLLWRNORM and POLLWRBAND will be generated. There is no functional distinction between these values.
The following is also enabled for GNU source:
POLLMSG - a system message is available; this is used for dnotify and possibly other functions. If POLLMSG is set then POLLIN and POLLRDNORM will also be set.
... However, the Linux man page for poll() states that Linux "knows about but does not use" POLLMSG.
The following additional values are not useful in events but may be returned in revents, i.e. they are implicitly polled:
POLLERR - an error condition has occurred
POLLHUP - hangup or disconnection of communications link
POLLNVAL - file descriptor is not open
The nfds argument should provide the size of the ufds array, and the timeout is specified in milliseconds.

The return from poll() is the number of file descriptors for which a watched event occurred (that is, an event which was set in the events field in the struct pollfd structure, or which was one of POLLERR, POLLHUP or POLLNVAL). The return may be 0 if the timeout was reached. The return is -1 if an error occurred, in which case errno will be set to one of the following:
EBADF - a bad file descriptor was given
ENOMEM - there was not enough memory to allocate file descriptor tables, necessary for poll() to function.
EFAULT - the specified array was not contained in the calling process's address space.
EINTR - a signal was received while waiting for events.
EINVAL - if the nfds is ridiculously large, that is, larger than the number of fds the process is allowed to have open. Note that this implies it may be unwise to add the same fd to the listen set twice.
Note that poll() exhibits the same problems in waiting for signals that select() does. There is a ppoll() function in more recent kernels (2.6.16+) which changes the timeout argument to a struct timespec * and which adds a sigset_t * argument to take the desired signal mask during the wait (this function is documented in the Linux man pages).
The poll call is inefficient for large numbers of file descriptors, because the kernel must scan the list provided by the process each time poll is called, and the process must scan the list to determine which descriptors were active. Also, poll exhibits the same problems in dealing with regular files as select() does (files are considered always ready for reading, except at end-of-file, and always ready for writing).
Epoll
On newer kernels - since 2.5.45 - a new set of syscalls known as the epoll interface (or just epoll) is available. The epoll interface works in essentially the same way as poll(), except that the array of file descriptors is maintained in the kernel rather than userspace. Syscalls are available to create a set, add and remove fds from the set, and retrieve events from the set. This is much more efficient than traditional poll() as it prevents the linear scanning of the set required at both the kernel and userspace level for each poll() call.
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epoll_create() is used to create a poll set. The size argument is an indicator only; it doesn not limit the number of fds which can be put into the set. The return value is a file descriptor (used to identify the set) or -1 if an error occurs (the only possible error is ENOMEM which indicates there is not enough memory or address space to create the set in kernel space). An epoll file descriptor is deleted by calling close() and otherwise acts as an I/O file descriptor which has input available if an event is active on the set.
epoll_ctl is used to add, remove, or otherwise control the monitoring of an fd in the set donated by the first argument, epfd. The op argument specifies the operation which can be any of:
EPOLL_CTL_ADD
add a file descriptor to the set. The fd argument specifies the fd to add. The event argument points to a struct epoll_event structure with the following members:
uint32_t events
a bitmask of events to monitor on the fd. The values have the same meaning as for the poll() events, though they are named with an EPOLL prefix: EPOLLIN, EPOLLPRI, EPOLLOUT, EPOLLRDNORM, EPOLLRDBAND, EPOLLWRNORM, EPOLLWRBAND, EPOLLMSG, EPOLLERR, and EPOLLHUP.
Two additional flags are possible: EPOLLONESHOT, which sets "One shot" operation for this fd, and EPOLLET, which sets edge-triggered mode (see the section on edge vs level triggered mechanisms; this flag allows epoll to act as either).
In one-shot mode, a file descriptor generates an event only once. After that, the bitmask for the file descriptor is cleared, meaning that no further events will be generated unless EPOLL_CTL_MOD is used to re-enable some events.
epoll_data_t data
this is a union type which can be used to specify additional data that will be assosciated with events on the file descriptor. It has the following members:
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
EPOLL_CTL_MOD
modify the settings for an existing descriptor in the set. The arguments are the same as for EPOLL_CTL_ADD.
EPOLL_CTL_DEL
remove a file descriptor from the set. The data argument is ignored.
The return is 0 on success or -1 on failure, in which case errno is set to one of the following:
EBADF - the epfd argument is not a valid file descriptor
EPERM - the target fd is not supported by the epoll interface
EINVAL - the epfd argument is not an epoll set descriptor, or the operation is not supported
ENOMEM - there is insufficient memory or address space to handle the request
The epoll_wait() call is used to read events from the fd set. The epfd argument identifies the epoll set to check. The events argument is a pointer to an array of struct epoll_event structures (format specified above) which contain both the user data associated with a file descriptor (as supplied with epoll_ctl()) and the events on the fd. The size of the array is given by the maxevents argument. The timeout argument specifies the time to wait for an event, in milliseconds; a value of -1 means to wait indefinitely.
In edge-triggered mode, an event is reported only once for each time the readiness state changes from inactive to active, that is, from the sitation being absent to being present. See discussion in the section on edge vs level triggered mechanisms.
The return is 0 on success or -1 on failure, in which case errno is set to one of:
EBADF - the epfd argument is not a valid file descriptor
EINVAL - epfd is not an epoll set descriptor, or maxevents is less than 1
EFAULT - the memory area occupied by the specified array is not accessible with write permissions
Note that an epoll set descriptor can be used much like a regular file descriptor. That is, it can be made to generate SIGIO (or another signal) when input (i.e. events) is available on it; likewise it can be used with poll() and can even be stored inside another epoll set.
Epoll is fairly efficient, but it still won't work with regular files. Also, adding/removing fds from a set might perform linearly on the size of the set (depending on the implementation in the kernel).
POSIX asynchronous I/O
The POSIX asynchronous I/O interface, which is documented in the GNU libc manual, would seem to be almost ideal for performing asynchronous I/O. After all, that's what it was designed for. But if you think that this is the case, you're in for bitter disappointment.
The documentation in the GNU libc manual (v2.3.1) is not complete - it doesn't document the "struct sigevent" structure used to control how notification of completed requests is performed. The structure has the following members:
int sigev_notify - can be set to SIGEV_NONE (no notification), SIGEV_THREAD (a thread is started, executing function sigev_notify_function), or SIGEV_SIGNAL (a signal, identified by sigev_signo, is sent). SIGEV_SIGNAL can be combined with SIGEV_THREAD_ID in which case the signal will be delivered to a specific thread, rather than the process. The thread is identified by the _sigev_un._tid member - this is an obviously undocumented feature and possibly an unstable interface.
void (*sigev_notify_function)(sigval_t) - if notification is done through a seperate thread, this is the function that is executed in that thread.
sigev_notify_attributes - if notification is done through a seperate thread, this field specifies the attributes of that thread.
int sigev_signo - if notification is to be performed by a signal, this gives the number of the signal.
sigval_t sigev_value - this is the parameter passed to either the signal handler or notification function. See real-time signals for more information.
Note that in particular, "sigev_value" and "sigev_notify_attributes" are not documented in the libc manual, and the types of none of the fields is specified.
Unfortunately POSIX AIO on linux is implemented at user level, using threads! (Actually, there is an AIO implementation in the kernel. I believe it's been in there since sometime in the 2.5 series. But it may have certain limitations - see here - I've yet to ascertain current status, but I believe it's not complete, and I don't believe Glibc uses it).
But there's a much more significant problem: The POSIX AIO API is totally screwed. The people who came up with it were on drugs or something. Really. I'll go through various issues, starting with the ones that aren't so bad and ending with the rool doozies.
It's not well explained in the Glibc manual, but partial writes/reads can occur just as with normal read()/write() calls. That's fine. You can find out how many bytes were actually read/written using aio_return(). Partial reads/writes don't really make sense for regular files but it's probably safest to assume that they can occur.
None of the documentation is particularly clear on whether you have to keep your AIO control block (struct aiocb) around after you've submitted an AIO request. The Open Group do say that you shouldn't let the aiocbp become an "illegal address" until completion, and that simultaneous operations using the same aiocb are probably going to cause grief, but for some reason they stop short of saying that you can't overwrite the aiocb at all. It's a pretty good bet, however, that you shouldn't.
lio_listio() is useless. At least, I can't think of any situations where you'd want to submit a whole bunch of requests at one time.
There is no way to use POSIX AIO to poll a socket on which you are listening for connections. It can only be used for actually reading or writing data. Ultimately, this should also be Ok because you can use ppoll() etc for the socket and wait for an asynchronous notification from the AIO mechanism, which is sort of ok (keep reading).
Of the notification methods, sending a signal would seem at the outset to be the only appropriate choice when large amounts of concurrent I/O are taking place. Although realtime signals could be used, there is a potential for signal buffer overflow which means signals could be lost; furthermore there is no notification at all of such overflow (one would think raising SIGIO in this case would be a good idea, but no, POSIX doesn't specify it, and glibc doesn't do it). What glibc does do is set an error on the AIO control block so that if you happen to check, you will see an error. Of course, you never will check because you'll never receive any notification of completion.
To use AIO with signal notifications reliably then, you need to check each and every AIO control block that is associated with a particular signal whenever that signal is received. For realtime signals it means that the signal queue should be drained before this is performed, to avoid redundant checking. It would be possible to use a range of signals and distribute the control blocks to them, which would limit the amount of control blocks to check per signal received; however, it's clear that ultimately this technique is not suitable for large amounts of highly concurrent I/O.
The other option for notification, using threads, is clearly stupid. If you're willing to spawn a thread per AIO request you may as well just use threads as a solution to begin with, and stick to regular blocking I/O. (Yes, technically, with AIO you only get one thread per active channel and so potentially you need a lot less threads than you would otherwise, however, you still potentially can get a lot of threads running all at once, and they do chew up memory. Also, it's not clear what happens if it's not possible to create a new thread at the time the event occurs).
aio_suspend(), while it might seem to solve the issue of notification, requires scanning the list of the aiocb structures by the kernel (to determine whether any of them have completed) and the userspace process (to find which one completed). That is to say, it has exactly the same problems as poll(). Also it has the potential signal race problem discussed previously (which can be worked around by having the signal handler write to a pipe which is being monitored by the aio_suspend call).
In short, it's a bunch of crap.
The ideal solution
... is yet to arrive. I'll examine the state of AIO support in recent kernel versions someday (its API looks, thankfully, a lot better than POSIX AIO, but it may still be lacking a lot of functionality).
There's occasionally talk of trying to improve the situation, but progress has been far, far slower than I'd like.
For the record, though, I think that the real solution:
Looks more like AIO than epoll. The API could be extended to allow waiting for I/O readiness as well as completion. It could also allow file locking, file opening etc. to become asynchronous operations.
Shares control blocks between the kernel and userspace, memory-mapped, in linked structures that avoid the need for scanning lists of block in either space. Obviously this needs a great deal of thought and planning, particularly to prevent security holes.
Provides a combined wait/suspend call which can wait for signals, I/O readiness events and I/O completions all at the same time, with a timeout.
Properly handles priority, in that I/O requests should be able to have a priority assigned (AIO does this already).
If I really had my way, heads would roll. Who the hell writes these Posix standards??
-- Davin McCall
Links and references:
Richard Gooch's I/O event handling (2002)
POSIX Asynchronous I/O for Linux - unclear whether this works with recent kernels
Buffered async IO on Jens Axboe's blog (Jan 2009)
The C10K problem by Dan Kegel. Good stuff, a bit out of date though. And what is C10K short for??
Fast UNIX Servers page by Nick Black, who informs me that C10K refers to Concurrent/Connections/Clients 10,000.
Yet to be discussed: eventfd, current kernel AIO support, syslets/threadlets, acall, timers including timerfd and setitimer, sendfile and variants.

Thread-Specific Data and Signal Handling in Multi-Threaded Applications

Here are the answers to questions about signal handling and taking care of global data when writing multi-threaded programs.



Perhaps the two most common questions I'm asked about multi-threaded programming (after “what is multi-threaded programming?” and “why would you want to do it?”) concern how to handle signals, and how to handle cases where two concurrent threads use a common function that makes use of global data, and yet the two threads need thread-specific data from that function. By definition, global data includes static local variables which are in truth a kind of global variable. In this article I'll explain how these questions can be dealt with in C programs using one of the POSIX (or almost POSIX) multi-threading packages available for Linux. I live in hope of the day when the most common question I'm asked about multi-threaded programming is, “Can we give you lots of money to write this simple multi-threaded application, please?” Hey—I can dream, can't I?
All the examples in this article make use of POSIX compliant functionality. To the best of my knowledge at the time I write this, there are no fully POSIX-compliant multi-threading libraries available for Linux. Which of the available libraries is best is something of a subjective issue. I use Xavier Leroy's LinuxThreads package, and the code fragments and examples were tested using version 0.5 of this library. This package can be obtained from http://pauillac.inria.fr/~xleroy/linuxthreads. Christopher Provenzano has a good user-level library, although the signal handling doesn't yet match the spec, and there were still a number of serious bugs the last time I used it. (These bugs, I believe, are being worked on.) Other library implementations are also available. Information on these and other packages can be found in the comp.programming.threads newsgroup and (to give a less than exhaustive list):
  • http://www.mit.edu:8001/people/proven/pthreads.html
  • http://www.aa.net/~mtp/PCthreads.html
  • ftp://ftp.cs.fsu.edu/pub/PART/PTHREADS
Thread-specific data
As I implied above, I use the term “global data” for any data which persists beyond normal scoping rules, such as static local variables. Given a piece of code like:
void foo(void)
{
        static int i = 1;
        printf( "%d\n", i );
        i = 2;
}
the first call to this function will print the value 1, and all subsequent calls will print the value 2, because the variable i and its value persist from one invocation of the function to the next, rather than disappearing in a puff of smoke as a “normal” local variable would. This, at least as far as POSIX threads are concerned, is global data.
It is commonly said (I've said it myself) that using global data is a bad practice. Whether or not this is true, it is only a rule of thumb. Certainly there are situations where using global data can avoid creating artificial circumstances. The previous article (Linux Journal Issue 34) explained how threads can share global data with careful use of mutual exclusion (mutex) functions to prevent one thread from accessing an item of global data while another thread is changing its value. In this article I will look at a different type of problem, using a real example from a recent project of mine.
Consider the case of a virtual reality system where a client makes several network socket connections to a server. Different types and priorities of data go down different sockets. High priority data, such as information about objects immediately in the field of view of the client, is sent down one socket. Lower priority data such as texture information, background sounds, or information about objects which are out of the current field of view, is sent down another socket to be processed whenever the client has available time. The server could create a collection of new threads every time a new client connects to the server, designating one thread for each of the sockets to be used to talk to each of the clients. Every one of these threads could use the same function to send a lump of data (not a technical term) to the client. The data to be sent details of the client it is to be sent to, the priority and type of data to be sent could all be held in global variables, and yet each thread will make use of different values. So how do we do it?
As a trivial example, suppose the only global data which our lump-sending function needs to use is an integer that indicates the priority of the data. In a non-threaded version, we might have a global integer called priority used as in Listing 1.


/* Code Example 1 */

/* a bit of global data */
int priority = 1;

void prepare_data( ... )
{
        ...
        priority = 1;
        ...
        lump_send( some_data );
        ...
}

void lump_send( data_t some_data )
{
        switch( priority )
        {
        case 1:  /* do one thing */
                break;
        case 2: /* do something else */
                break;
        }
}

In the multi-threaded version we don't have a global integer, instead we have a global key to the integer. It is through the key that the data can be accessed by means of a number of functions:
  1. pthread_key_create() to prepare the key for use
  2. pthread_setspecific() to set a value to thread-specific data
  3. pthread_getspecific() to retrieve the current value
pthread_key_create() is called once, generally before any of the threads which are going to use the key have been created. pthread_getspecific() and pthread_setspecific() never return an error if the key that is used as an argument has not been created. The result of using them on a key which has not been created is undefined. Something will happen, but it could vary from system to system, and it can't be caught simply by using good error handling. This is an excellent source of bugs for the unwary. So our multi-threaded version might look like Listing 2.


/* Code Example 2 */

#include <pthread.h>

/* most threads that this program will create */
#define MAX_NUMBER_OF_THREADS ...

/* function prototypes */
void* client_thread( void* );
void prepare_data( void );
void lump_send( data_t );

/* global key to the thread specific data */
pthread_key_t priority_key;

int main( void )
{
        int n;

        pthread_t
           thread_id[MAX_NUMBER_OF_THREADS];
        ...
        /* create the thread specific data key
         * before creating the threads */
        pthread_key_create( &priority_key, NULL );
        ...
        /* create thread that will use the key */
        pthread_create( &thread_id[n], NULL,
            client_thread, NULL );
        ...
}

void* client_thread( void* arg )
{
        ...
        prepare_data();
        ...
}

void prepare_data( void )
{
        data_t some_data;
        ...
        /* store the value 1.  This value is
         * globally available, but only to this
         * thread */
        pthread_setspecific( priority_key,
            (void*)1 );
        ...
        lump_send( some_data );
        ...
}

void lump_send( data_t some_data )
{
        /* get this thread's global data from
         * priority_key */
        switch( (int)pthread_getspecific(
             priority_key ))
        {
        case 1:  /* do one thing */
                break;
        case 2: /* do something else */
                break;
        }
}


There are a few things to note here:
  1. The implementation of POSIX threads can limit the number of keys a process may use. The standard states that this number must be at least 128. The number available in any implementation can be found by looking at the macro PTHREAD_KEYS_MAX. According to this macro, LinuxThreads currently allows 128 keys.
  2. The function pthread_key_delete() can be used to dispose of keys that are no longer needed. Keys, like all “normal” data items, vanish when the process exits, so why bother deleting them? Think of key handling as being similar to file handling. An unsophisticated program need not close any files that it has opened, as they will be automatically closed when the program exits. But since there is a limit to the number of files a program can have open at one time, the best policy is to close files not currently being used so that the limit is not exceeded. This policy also works well for key handling, as you may be limited in the number of thread-specific data keys a process may have.
  3. pthread_getspecific() and pthread_setspecific() access thread-specific data as void*pointers. This ability can be used directly (as in Listing 2), if the data item to be accessed can be cast as type void*, e.g., an int in most, but not necessarily all, implementations. However, if you want your code to be portable or if you need to access larger data objects, then each thread must allocate sufficient memory for the data object, and store the pointer to the object in the thread-specific data rather than storing the data itself.
  4. If you allocate some memory (using the standard function malloc(), for instance) for your thread-specific data, and the thread exits at some point, what happens to the allocated memory? Nothing happens, so it leaks, and this is bad. This is the situation where the extra parameter in the pthread_key_create() function comes into use. This parameter allows you to specify a function to call when a thread exits, and you use that function to free up any memory that has been allocated. To prevent a waste of CPU time, this destructor function is called only in the case where a thread has made use of that particular key. There's little point in tidying up for a thread that has nothing to be tidied. When a thread exits because it called one of the functions exit()_exit() or abort(), the destructor function is not called. Also, note that pthread_key_delete() does not cause any destructors to be called, that using a key that has been deleted doesn't have a defined behavior, and that pthread_getspecific() and pthread_setspecific() don't return any error indications. Tidy up your keys carefully. One day you'll be glad you did. So a better version of our code is Listing 3.

/* Code Example 3 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUMBER_OF_KEYS_WE_USE ...
#define MAX_NUMBER_OF_THREADS ...

/* global key to the thread specific data */
pthread_key_t priority_key;

/* function prototypes */
void* client_thread( void* );
void prepare_data( void );
void lump_send( data_t );

int main( void )
{
        int n;
        pthread_t
           thread_id[MAX_NUMBER_OF_THREADS];
        ...
        /* check that the implementation can cope
         * with all the keys we need */
        if ( NUMBER_OF_KEYS_WE_USE >
             PTHREAD_KEYS_MAX ) {
                fprintf( stderr,
                "Not enough keys available\n");
                exit( -1 );
        }
        /* create the keys that we need.  We're
         * going to use "malloc()" to grab
         * some memory and point the thread specific
         * data at it. If the thread dies, we'd like
         *  the system to use "free()" to
         *  release that memory for us
         */
        pthread_key_create( &priority_key, free );
        ...
        /* create the threads */
        pthread_create( &thread_id[n], NULL,
            client_thread, NULL );
        ...
}

void* client_thread( void* arg )
{
        /* grab enough memory to store an int, and
         * store a pointer to that memory as thread
         * specific data
         */
        pthread_setspecific( priority_key,
               malloc( sizeof( int ) ) );
        ...
        prepare_data();
        ...
}

void prepare_data( void )
{
        data_t some_data;
        ...
        /* store the priority value in the
         *  memory pointed to by the thread
         *  specific data
         */

        *((int*)pthread_getspecific(
        priority_key )) = 1;
        ...
        lump_send( some_data );
        ...
}

void lump_send( data_t some_data )
{
        /* act on the value stored in the memory
         *  pointed to by the thread specific data
         */
        switch( *((int*)pthread_getspecific(
             priority_key )) )
        {
        case 1:  /* do one thing */
                break;
        case 2: /* do something else */
                break;
        }
}


Some of this code might look a little strange at first sight. Using pthread_getspecific() to store a thread specific value? The idea is to get the memory location this thread is to use, and then the thread specific value is stored there.
Even if global data is anathema to you, you might still have good use for thread-specific data. In particular, you might need to write a multi-threaded version of some existing library code that is also going to be used in a non-threaded program. A good simple example is making a version of the standard C libraries fit for use by multi-threaded programs. That friend of all C programmers, errno, is a global variable that is commonly set by library functions to indicate what went wrong during a function call. If two threads call functions which both set errno to different values, at least one of the threads is going to get the wrong information. This is solved by having thread-specific data areas for errno, rather than one global variable used by all threads.



Signal Handling
Many people find signal handling in C to be a bit tricky at the best of times. Multi-threaded applications need a little extra care when it comes to signal handling, but once you've written two programs, you'll wonder what all the fuss was about—trust me. And if you start to panic, remember—deep, slow breaths.
A quick re-cap of what signals are. Signals are the system's way of informing a process about various events. There are two types of signals, synchronous and asynchronous.
Synchronous signals are a result of a program action. Two examples are:
  1. SIGFPE, floating-point exception, is returned when the program tries to do some illegal mathematical operation such as dividing by zero.
  2. SIGSEGV, segmentation violation, is returned when the program tries to access an area of memory outside the area it can legally access.
Asynchronous signals are independent of the program. For example, the signal sent when the user gives the kill command.
In non-threaded applications there are three usual ways of handling signals:
  1. Pretend they don't exist, perhaps the most common policy, and quite adequate for lots of simple programs—at least until you want your program to be reliable and useful.
  2. Use signal() to set up a signal handler—nice and simple, but not very robust.
  3. Use the POSIX signal handling functions such as sigaction() and sigprocmask() to set up a signal handler or to ignore certain signals—the “proper” method.
If you choose the first option, then signals will have some default behavior. Typically, this default behavior will cause the program to exit or cause the program to ignore the signal, depending on what the signal is. The latter two options allow you to change the default behavior for each signal type—ignore the signal, cause the program to exit or invoke a signal-handling function to allow your program to perform some special processing. Avoid the use of the old-style signal() function. Whether you're writing threaded or non-threaded applications, the extra complications of the POSIX-style functions are worth the effort. Note that the behavior of sigprocmask(), which sets a signal mask for a process, is undefined in a multi-threaded program. There is a new function, pthread_sigmask(), that is used in much the same way as sigprocmask(), but it sets the signal mask only for the current thread. Also, a new thread inherits the signal mask of the thread that created it; so a signal mask can effectively be set for an entire process by calling pthread_sigmask() before any threads are created.
In a multi-threaded application, there is always the question of which thread the signal will actually be delivered to. Or does it get delivered to all the threads?
To answer the last question first, no. If one signal is generated, one signal is delivered, so any single signal will only be delivered to a single thread.
So which thread will get the signal? If it is a synchronous signal, the signal is delivered to the thread that generated it. Synchronous signals are commonly managed by having an appropriate signal handler set up in each thread to handle any that aren't masked. If it is an asynchronous signal, it could go to any of the threads that haven't masked out that signal usingsigprocmask(). This makes life even more complicated. For instance, suppose your signal handler must access a global variable. This is normally handled quite happily by using mutex, as follows:
void signal_handler( int sig )
{
        ...
        pthread_mutex_lock( &mutex1 );
        ...
        pthread_mutex_unlock( &mutex1 );
        ...
}
Looks fine at first sight. However, what if the thread that was interrupted by the signal had just itself locked mutex1? The signal_handler() function will block, and will wait for the mutex to be unlocked. And the thread that is currently holding the mutex will not restart, and so will not be able to release the mutex until the signal handler exits. A nice deadly embrace.
So a common way of handling asynchronous signals in a multi-threaded program is to mask signals in all the threads, and then create a separate thread (or threads) whose sole purpose is to catch signals and handle them. The signal-handler thread catches signals by calling the functionsigwait() with details of the signals it wishes to wait for. To give a simple example of how this might be done, take a look at Listing 4.


/* Code Example 4 */

#include <pthread.h>
#include <signal.h>

void* sig_handler( void* );

/* global variable used to indicate what signal
 * (if any) has been caught
 */
int handled_signal = -1;

/* mutex to be used whenever accessing the above
 * global data */
pthread_mutex_t sig_mutex = PTHREAD_MUTEX_INITIALIZER;

int main(void )
{
        sigset_t signal_set;
        pthread_t sig_thread;

        /* block all signals */
        sigfillset( &signal_set );
        pthread_sigmask( SIG_BLOCK, &signal_set,
                NULL );

        /* create the signal handling thread */
        pthread_create( &sig_thread, NULL,
                sig_handler, NULL );

        for (;;) {
            /* whatever you want your program to
             * do... */

                /* grab the mutex before looking
                 * at handled_signal */
                pthread_mutex_lock( &sig_mutex );

                /* look to see if any signals have
                 * been caught */
                switch ( handled_signal )
                {
                case -1:
                  /* no signal has been caught
                   * by the signal handler */
                  break;

                case 0:
                printf("The signal handler caught"
                " a signal I'm not interested in "
                "(%d)\n",
                 handled_signal );
                 handled_signal = -1;
                 break;

                case SIGQUIT:
                printf("The signal handler caught"
                " a SIGQUIT signal!\n" );
                 handled_signal = -1;
                 break;

                case SIGINT:
                printf(
                "The signal handler caught"
                " a SIGINT signal!\n" );
                 handled_signal = -1;
                 break;
                }
                /* remember to release mutex */
                pthread_mutex_unlock(&sig_mutex);
        }
}

void* sig_handler( void* arg )
{
        sigset_t signal_set;
        int sig;

        for(;;) {
                /* wait for any and all signals */
                sigfillset( &signal_set );
                sigwait( &signal_set, &sig );

                /* when we get this far, we've
                 * caught a signal */

                switch( sig )
                {
                /* whatever you need to do on
                 * SIGQUIT */
                case SIGQUIT:
                  pthread_mutex_lock(&sig_mutex);
                  handled_signal = SIGQUIT;
                  pthread_mutex_unlock(&sig_mutex);
                  break;

                /* whatever you need to do on
                 * SIGINT */
                 case SIGINT:
                  pthread_mutex_lock(&sig_mutex);
                  handled_signal = SIGINT;
                  pthread_mutex_unlock(&sig_mutex);
                  break;

                /* whatever you need to do for
                 * other signals */
                default:
                  pthread_mutex_lock(&sig_mutex);
                  handled_signal = 0;
                  pthread_mutex_unlock(&sig_mutex);
                  break;
                }
        }
        return (void*)0;
}


As mentioned earlier, a thread inherits its signal mask from the thread which creates it. Themain() function sets the signal mask to block all signals, so all threads created after this point will have all signals blocked, including the signal-handling thread. Strange as it may seem at first sight, this is exactly what we want. The signal-handling thread expects signal information to be provided by the sigwait() function, not directly by the operating system. sigwait() will unmask the set of signals that are given to it, and then will block until one of those signals occurs.
Also, you might think that this program will deadlock, if a signal is raised while the main thread holds the mutex sig_mutex. After all, the signal handler tries to grab that same mutex, and it will block until that mutex comes free. However, the main thread is ignoring signals, so there is nothing to prevent another thread from gaining control while the signal handling thread is blocked. In this case, sig_handler() hasn't caught a signal in the usual, non-threaded sense. Instead it has asked the operating system to tell it when a signal has been raised. The operating system has performed this function, and so the signal handling thread becomes just another running thread.


Differences in Signal Handling between POSIX Threads and LinuxThreads
Listing 4 shows how to deal with signals in a multi-threading environment that handles threads in a POSIX compliant way.
Personally, I like the kernel-level package “LinuxThreads” that makes use of Linux 2.0's clone() system call to create new threads. At some point in the future, the clone() call may implement theCLONE_PID flag which would allow all the threads to share a process ID. Until then each thread created using “LinuxThreads” (or any other packages which chooses to use clone() to create threads) will have its own unique process ID. As such, there is no concept of sending a signal to “the process.” If one thread calls sigwait() and all other threads block signals, only those signals which are specifically sent to the sigwait()-ing thread will be processed. Depending on your application, this could mean that you have no choice other than to include an asynchronous signal handler in each of the threads.
Summary
Thread specific data is easy to use—far easier than many people's first experiences may suggest. In a way, this ease of use is a disadvantage, since very often there are more elegant solutions to a problem. But in times of need, thread specific data is your friend.
On the other hand, signal handling in anger can be a little hairy. Anyone who thinks otherwise has overlooked something—either that or they're far too clever for their own good. Make life easier for yourself by consigning all the handling of asynchronous signals to one thread that sits on sigwait().
Martin McCarthy discovered multi-threaded programming while writing the server for a high-speed, multi-user, distributed, virtual-reality system. Of course, he only took that job so that he could squeeze as many buzzwords into his job description as possible. He can be reached at marty@ehabitat.demon.co.uk.

Linux Signals for the Application Programmer

Signals are a fundamental method for interprocess communication ad are used in everything from network servers to media players. Here's how you can use them in your applications.
A good understanding of signals is important for an application programmer working in the Linux environment. Knowledge of the signaling mechanism and familiarity with signal-related functions help one write programs more efficiently.
An application program executes sequentially if every instruction runs properly. In case of an error or any anomaly during the execution of a program, the kernel can use signals to notify the process. Signals also have been used to communicate and synchronize processes and to simplify interprocess communications (IPCs). Although we now have advanced synchronization tools and many IPC mechanisms, signals play a vital role in Linux for handling exceptions and interrupts. Signals have been used for approximately 30 years without any major modifications.
The first 31 signals are standard signals, some of which date back to 1970s UNIX from Bell Labs. The POSIX (Portable Operating Systems and Interface for UNIX) standard introduced a new class of signals designated as real-time signals, with numbers ranging from 32 to 63.
A signal is generated when an event occurs, and then the kernel passes the event to a receiving process. Sometimes a process can send a signal to other processes. Besides process-to-process signaling, there are many situations when the kernel originates a signal, such as when file size exceeds limits, when an I/O device is ready, when encountering an illegal instruction or when the user sends a terminal interrupt like Ctrl-C or Ctrl-Z.
Every signal has a name starting with SIG and is defined as a positive unique integer number. In a shell prompt, the kill -l command will display all signals with signal number and corresponding signal name. Signal numbers are defined in the /usr/include/bits/signum.h file, and the source file is /usr/src/linux/kernel/signal.c.
A process will receive a signal when it is running in user mode. If the receiving process is running in kernel mode, the execution of the signal will start only after the process returns to user mode.
Signals sent to a non-running process must be saved by the kernel until the process resumes execution. Sleeping processes can be interruptible or uninterruptible. If a process receives a signal when it is in an interruptible sleep state, for example, waiting for terminal I/O, the kernel will awaken the process to handle the signal. If a process receives a signal when it is in uninterruptible sleep, such as waiting for disk I/O, the kernel defers the signal until the event completes.
When a process receives a signal, one of three things could happen. First, the process could ignore the signal. Second, it could catch the signal and execute a special function called a signal handler. Third, it could execute the default action for that signal; for example, the default action for signal 15, SIGTERM, is to terminate the process. Some signals cannot be ignored, and others do not have default actions, so they are ignored by default. See the signal(7) man page for a reference list of signal names, numbers, default actions and whether they can be caught.
When a process executes a signal handler, if some other signal arrives the new signal is blocked until the handler returns. This article explains the fundamentals of the signaling mechanism and elaborates on signal-related functions with syntax and working procedures.
Signals inside the Kernel
Where is the information about a signal stored in the process? The kernel has a fixed-size array of proc structures called the process table. The u or user area of the proc structure maintains control information about a process. The major fields in the u area include signal handlers and related information. The signal handler is an array with each element for each type of signal being defined in the system, indicating the action of the process on the receipt of the signal. The proc structure maintains signal-handling information, such as masks of signals that are ignored, blocked, posted and handled.
Once a signal is generated, the kernel sets a bit in the signal field of the process table entry. If the signal is being ignored, the kernel returns without taking any action. Because the signal field is one bit per signal, multiple occurrences of the same signal are not maintained.
When the signal is delivered, the receiving process should act depending on the signal. The action may be terminating the process, terminating the process after creating a core dump, ignoring the signal, executing the user-defined signal handler (if the signal is caught by the process) or resuming the process if it is temporarily suspended.
The core dump is a file called core, which has an image of the terminated process. It contains the process' variables and stack details at the time of failure. From a core file, the programmer can investigate the reason for termination using a debugger. The word core appears here for a historical reason: main memory used to be made from doughnut-shaped magnets called inductor cores.
Catching a signal means instructing the kernel that if a given signal has occurred, the program's own signal handler should be executed, instead of the default. Two exceptions are SIGKILL and SIGSTOP, which cannot be caught or ignored.
sigset_t is a basic data structure used to store the signals. The structure sent to a process is a sigset_t array of bits, one for each signal type:
typedef struct {
                   unsigned long sig[2];
                }  sigset_t;
Because each unsigned long number consists of 32 bits, the maximum number of signals that may be declared in Linux is 64 (according to POSIX compliance). No signal has the number 0, so the other 31 bits in the first element of sigset_t are the standard first 31 signals, and the bits in the second element are the real-time signal numbers 32-64. The size of sigset_t is 128 bytes.


Handling Signals
There are many system calls and signal-supported library functions, which provide an easy and efficient way of handling the signals in a process. We start with the standard old signal system call, then we discuss some useful functions like sigaction, sigaddset, sigemptyset, sigdelset, sigismember and kill.
The Signal System Call
The signal system call is used to catch, ignore or set the default action of a specified signal. It takes two arguments: a signal number and a pointer to a user-defined signal handler. Two reserved predefined signal handlers are available in Linux: SIG_IGN and SIG_DFL. SIG_IGN will ignore a specified signal, and SIG_DFL will set the signal handler to the default action for that signal (see man 2 signal).
On success, the system call returns the previous value of the signal handler for the specified signal. If the signal call fails, it returns SIG_ERR. Listing 1 explains how to catch, ignore and set the default action of SIGINT. Try pressing Ctrl-C, which sends SIGINT, during each part.
Listing 1. Catching and Ignoring a Signal


Listing 1. Catching and Ignoring a Signal

#include <signal.h>

void my_handler (int sig); /* function prototype */

int main ( void ) {

/* Part I: Catch SIGINT */
    signal (SIGINT, my_handler);
    printf ("Catching SIGINT\n");
    sleep(3);
    printf (" No SIGINT within 3 seconds\n");

/* Part II: Ignore SIGINT */
    signal (SIGINT, SIG_IGN);
    printf ("Ignoring SIGINT\n");
    sleep(3);
    printf ("No SIGINT within 3 seconds\n");

/* Part III: Default action for  SIGINT */
    signal (SIGINT, SIG_DFL);
    printf ("Default action for SIGINT\n");
    sleep(3);
    printf ("No SIGINT within 3 seconds\n");
    return 0;
}

/* User-defined signal handler function */
void my_handler (int sig) {
    printf ("I got SIGINT, number %d\n", sig);
    exit(0);
}
sigaction
The sigaction system call can be used instead of signal because it has lot of control over a given signal. The syntax of sigaction is:
int sigaction ( int signum,
                const struct sigaction *act,
                struct sigaction *oldact);
The first argument, signum, is a specified signal; the second argument, sigaction, is used to set the new action of the signal signum; and the third argument is used to store the previous action, usually NULL.
The sigaction structure is defined as:
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
}
The members of the sigaction structure are described as follows.
sa_hander: a pointer to a user-defined signal handler or predefined signal handler (SIG_IGN or SIG_DFL).
sa_mask: specifies a mask of signals when the signal is handled. To avoid the blocking of signals, the SA_NODEFER or SA_NOMASK flags can be used.
sa_flags: specifies the action of signal. Sets of flags are available for controlling the signal in a different manner. More than one flag can be used by ORing:
  • SA_NOCLDSTOP: if we specify the SIGCHLD signal, when the child has stopped its execution it does not receive notification.
  • SA_ONESHOT or SA_RESETHAND: restores the default action of the signal after the user-defined signal handler is executed. To avoid setting the default action, SA_RESTART can be used.
  • SA_NOMASK or SA_NODEFER prevents masking the signal. SA_SIGINFO is used to receive signal-related information.
sa_sigaction: if the SA_SIGINFO flag is used in sa_flags, instead of specifying the signal handler in sa_handler, sa_sigaction should be used.
sa_sigaction is a pointer to a function that takes three arguments, not one as sa_handler does, for example:
void my_handler (int signo, siginfo_t *info,
                     void *context)
Here, signo is the signal number, and info is a pointer to the structure of type siginfo_t, which specifies the signal-related information; and context is a pointer to an object of type ucontext_t, which refers to the receiving process context that was interrupted with the delivered signal.
Listing 2 is similar to Listing 1 but uses the sigaction system call instead of the signal system call. Listing 3 explains signal-related information using the SIG_INFO flag.
Listing 2. Same as Listing 1, but with Sigaction


Listing 2. Same as Listing 1, but with sigaction

#include <signal.h>
#include <stdio.h>

void my_handler (int sig); /* function prototype */

int main ( void ) {

    struct sigaction my_action;

/* Part I: Catch SIGINT */

    my_action.sa_handler = my_handler;
    my_action.sa_flags = SA_RESTART;
    sigaction (SIGINT, &my_action, NULL);
    printf ("Catching SIGINT\n");
    sleep(3);
    printf (" No SIGINT within 3 seconds\n");


/* Part II: Ignore SIGINT */

   my_action.sa_handler = SIG_IGN;
   my_action.sa_flags = SA_RESTART;
   sigaction (SIGINT, &my_action, NULL);
   printf ("Ignoring SIGINT\n");
   sleep(3);
   printf (" Sleep is over\n");


/* Part III: Default action for  SIGINT */

  my_action.sa_handler = SIG_DFL;
  my_action.sa_flags = SA_RESTART;
  sigaction (SIGINT, &my_action, NULL);
  sleep(3);
  printf ("No SIGINT within 3 seconds\n");
}

void my_handler (int sig) {
    printf ("I got SIGINT, number %d\n", sig);
    exit(0);
}



Listing 3. Using SA_SIGINFO and sa_sigaction to Extract Information from a Signal


Listing 3. Using SA_SIGINFO and sa_sigaction to Extract Information from a Signal

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <bits/siginfo.h>
#include <stdio.h>

void handler (int signo, siginfo_t *info,
              void *context);

main () {

   struct sigaction my_action;

   my_action.sa_flags = SA_SIGINFO;
   my_action.sa_sigaction = handler;

   sigaction(SIGINT, &my_action, NULL);

   printf ("Catching SIGINT\n");
   sleep(5);
   printf ("Done.\n");
}

void handler (int signo, siginfo_t *info,
              void *context)
 {
    printf ("Signal number: %d\n", info->si_signo);

 /* Elements of the siginfo_t structure are listed
    in man 2 sigaction.
 */
}


Sending Signals
Until now, we've been pressing Ctrl-C to send SIGINT from the shell. To do it from a program, use the kill system call, which accepts two arguments, process ID and signal number:
int kill ( pid_t process_id, int signal_number );
If the pid is positive, the signal is sent to a particular process. If the pid is negative, the signal is sent to the process whose group ID matches the absolute value of pid.
As you might expect, the kill command, which exists as a standalone program (/bin/kill) and is also built into bash (try help kill) uses the kill system call to send a signal.
Not all processes can send signals to each other. In order for one process to send a signal to another, either the sender must be running as root, or the sender's real or effective user ID must be the same as the real or saved ID of the receiver. This means your shell, running as you, can signal a setuid program that you started, but that is now running as root, for example:
cp /bin/sleep ~/rootsleep
sudo chmod u+s ~/rootsleep
./rootsleep 40
killall rootsleep
rm ~/rootsleep
A normal user can't send signals to system processes such as swapper and init.
You also can use kill to find out if a process exists. Specify a signal number of 0, and if the process exists, the kill returns zero; if it doesn't, kill returns -1.
Listing 4. Programs to Send and Receive SIGINT


Listing 4. Programs to Send and Receive SIGINT

#include <signal.h>

main ( ) {
    int process_id;
    printf ("Enter process_id which you want "
            "to send a signal : ");
    scanf ("%d", &process_id);

   if (!(kill ( process_id, SIGINT)))
       printf ("SIGINT sent to %d\n", process_id);
   else if (errno == EPERM)
       printf ("Operation not permitted.\n");
   else
       printf ("%d doesn't exist\n", process_id);
}

/* Listing 4a. This program will run until it
   receives SIGINT */

#include <signal.h>

 main ( ) {
   printf (" This process id is %d. "
   "Waiting for SIGINT.\n", getpid());
   for (;;);
}

Listings 4 and 4a explain how to use the kill system call. First, execute the 4a program in one window and get its process ID. Now, run the Listing 4 program in another window and give the 4a example's pid as the input.
This article should help you understand the fundamental concept of a signal and some of its importance. Try the sample programs, and see the man pages for the system calls and the references in Resources for more information.
Resources
email: balasubramanian.thangaraju@wipro.com
Dr B. Thangaraju received a PhD in Physics and worked as a research associate for five years at the Indian Institute of Science, India. He is presently working as a manager at Talent Transformation, Wipro Technologies, India. He has published many research papers in renowned international journals. His current areas of research, study and knowledge dissemination are the Linux kernel, device drivers and real-time Linux.

2010-06-25

Eclipse Development Environment on Ubuntu 10.04

Introduction

This is to create the Eclipse Development Environment on Ubuntu 10.04 mainly for:

  • C/C++ development (local and cross platform)
  • Shell programming
  • Python development
  • Work with Bazaar Version Control
Installation of Eclipse CDT

Ubuntu 10.04 software center have Eclipse platform package, but doesn't have CDT package. So better to install the CDT package directly.

  1. Download the Eclipse CDT package from eclipse website: www.eclipse.org.
  2. Unzip it to '/usr/share'. Now it can be started from running '/usr/local/eclipse/eclipse'.
  3. Create an Application Launcher on the Gnome top panel pointing to the executable '/usr/local/eclipse/eclipse'.

Install Bazaar Plugin

  1. From Eclipse, open the dialog from 'Help' -> 'Install New Software'.
  2. Click button 'Add' to add the Bazaar plugin update site: http://verterok.com.ar/bzr-eclipse/update-site/
  3. Select the package and go ahead to install it.
Note this is the normal process to install Eclipse plugins. The key is to know the update site URL.

With the Bazaar plugin installed, you can create Eclipse project by checking out a new branch or using an existing branch checkout.


Install ShellEd Plugin

ShellEd plugin allows editing Shell Script. It requires Linux Tools Man Page Viewer plugin.

  1. Follow the normal plugin installation process, and the Linux Tools update site URL is: http://download.eclipse.org/technology/linuxtools/update.
  2. ShellEd doesn't have an update site URL, so download the package from: http://sourceforge.net/projects/shelled/
  3. Unzip it in the local directory, for example: /home/one/Downloads/ShellEd.
  4. From Eclipse, open the dialog from 'Help' -> 'Install New Software'.
  5. Click button 'Add' to add the ShellEd update site as 'Local'. Point it to the folder containing the unzipped package: '/home/one/Downloads/ShellEd'.
  6. Select the package and go ahead to install it.
Install PyDev Plugin

PyDev plugin's update site URL is: http://pydev.org/updates


2010-05-26

数据结构与算法好书推荐(转贴)

如果计算机系只开三门课,那么这三门课就一定是:离散数学,数据结构与算法,编译原理。如果只开一门课,那剩下的就一定是:数据结构与算法。Niklaus Wirth说:算法+数据结构=程序,不说废话了,下面列出一份数据结构算法书目,先从最著名的说起

A
原书名:The Art of Computer Programming
中文名:计算机程序设计艺术
作者:Donald E.Knuth
难度:*****
个人评价:*******
推荐程度:****
本书是算法分析的经典名作(用经典不太恰当,应该是圣经或史诗),被科学美国人列为20世纪12大科学名著之一(和Dirac的量子力学,Einstein 的广义相对论,von Neumann 的博弈论的著作等齐名)。其亮点在于其超乎寻常的数学技巧,要求读者拥有极高的数学修养,只要你坚持忍耐,一旦读懂了,你的算法和程序设计水平也会达到更高的档次,你会对程序设计有一种截然不同的体会和领悟,就是“道”(Tao)。书的排版很漂亮(得益于作者的Tex系统),看起来很舒服。作者的文笔很好,写得生动活泼,读起来荡气回肠(英文版)。习题多且精华,触及算法和程序本质,书后有几乎所有习题的答案(占了整全书篇幅的1/4),书中的分析方法体现了作者严谨的风格。不过本书的程序不是用我们熟悉的高级语言描述的,而是作者设计的MIX语言。整套书原计划出七卷,现在出了三卷:基本算法,半数值算法,排序和搜索,第四卷组合算法跳票了20年,Knuth称在2008年推出。本书有中文版,不过建议读者选用英文版,因为都学到这个程度了,英语应该不会有大困难了。引用一句话“在我们的有生之年,可能会看到C++的消亡,但Knuth和他的程序设计艺术,将永远留在我们的心里。”


B
原书名:Introduction to Algorithms
中文名:算法导论
作者:Thomas H.Cormen,Charles E.Leiserson,Ronald L.Rivest,Clifford Stein
难度:***
个人评价:*****
推荐程度:*****
本书俗称CLRS(作者名字的简写),算法的经典教材,堪称算法分析著作中的“独孤九剑”。作者之一Ronald L.Rivest 由于其在公开秘钥密码算法RSA上的贡献获得了ACM图灵奖。全书内容全面,结构清晰,6个部分1000多页把数据结构算法的主要内容都包含了。作者用图表,伪码解释每一个算法,通俗易懂而不失严谨性,英文比较简单,语言流畅,因此,与TAOCP相比,这本书更适合初学者,不要求读者拥有很强的数学背景和丰富的编程经验。书中习题安排合理,难度适中,在网上有全部习题的答案,网上还有作者在MIT讲述本书的课程的录像,可谓资源丰富,值得注意的是书中每一章后面都有一个Chapter notes,了解一下历史,看一下作者推荐的材料是不错的(如果你能找到的话)。


C
原书名:The Design and Analysis of Computer Algorithms
中文名:算法设计与分析
作者:Aho,Hopcroft,Ullman
难度:****
个人评价:*****
推荐程度:*****
该书写于1976年,作者Hopcroft是 1986年ACM图灵奖得主,这三个人写过很多书,大多数都是经典,于一般的算法书不同,该书侧重于证明算法的正确性和复杂性,而不是怎样实现和应用算法,叙述上更加形式化,属于定义-引理-定理的数学书风格,认真研究一下里面的证明能大大提高理论水平。如果你看完了CLRS或其他数据结构入门书,要深入学习算法,但TAOCP看起来又太吃力的话,这本比较适合。最后一点是书中的习题很精华,即使你不看这本书,做一下里面的习题也是非常有意思的


D
原书名:Data Structures and Algorithms
中文名:数据结构与算法
作者:Aho,Hopcroft,Ullman
难度:***
个人评价:****
推荐程度:****
上面那本书的姐妹篇,内容就简单很多了,该书写法有个特点就是每一个主题都从一个基本的观念出发,然后再逐渐深入讨论,这样做能使解释更清晰,富有启发性。不过这本书写于20年前,所以有一些高级内容如红黑树是没有的,拿这本书做教材的读者最好同时拿一本较新的来做参考。


E
原书名:Algorithms in C,Algorithms in C++,Algorithms in Java
中文名:算法I-IV(C实现),算法V(C实现)(C++实现)(Java实现)
作者:Robert Sedgewick
难度:***
个人评价:****
推荐程度:****
Robert Sedgwick是Knuth的学生,现在是princeton的教授。这是三个系列,与上面用伪码描述算法不同,本书用现今流行的语言C,C++,Java描述.那么选拿哪一种语言好呢?从算法的角度看,任何高级语言都是没区别的,虽然实现算法的时候,到了语言相关的层面会有一些细微区别,但影响不大。个人推荐C++的,因为价钱最便宜:)。本书的一个特点就是例子取得很好,代码很清晰。有中文版


F
原书名:Algorithms Design Techniques and Analysis
中文名:算法设计技巧与分析
作者:M.H.Alsuwaiyel
难度:****
个人评价:****
推荐程度:****
这本书对一般算法书较少涉及的概率算法和近似算法作了重要的补充


G
原书名:Introduction to The Design & Analysis of Algorithms
中文名:算法设计与分析基础
作者:Anany Levitin
难度:***
个人评价:****
推荐程度:****
算法书的另一种写法,以方法为主线,如Brute-Force, Divide-and-Conquer, Greedy techniques,书里面有很多有趣的习题


H
原书名:Data Structures, Algorithms, and Applications in C++
中文名:数据结构算法与应用-C++语言描述
作者:Sartej Sahni 译者:汪诗林等
难度:***
个人评价:***
推荐程度:***
不少人推荐这本书,但我个人觉得这书不怎么样,中文版翻译水平差强人意,数据结构算法部分把该讲的都讲了,但没什么突出的地方,反而C++倒说了不少,代码的水平也不怎么样。从ACCU的评价上看,书中的实现与BOOST和STL相比相去甚远。不过这书有很多实际问题,可以看一看。


I
原书名:
中文名:算法与数据结构
作者:傅清祥 王晓东
难度:***
个人评价:****
推荐程度:****
这本是国人写的最好的数据结构算法书之一,讲得很细致。最后的三章:复杂性,并行算法,高级专题有一些有趣的东西,是这些高级内容的很好的导论。


J
原书名:
中文名:数据结构(C语言版)
作者:严蔚敏 吴伟民
难度:***
个人评价:***
推荐程度:***
另一本写的较好的中文数据结构算法书,这本书特别适合考试用(没有任何轻视的意思)



上面的书适合哪些人(我只是学生,这只是个人意见)
做学术研究:A+C+F
学过初级课程要深入:C+F+(I后三章)
在职或讲求实用:E
入门:B或D
程序设计竞赛:B+G+(I前八章)
考研或程序员考试:J

2010-05-05

Java学习


最近论坛上看到好几个朋友都在问,如何学习 Java的问题,“我已经学习了J2SE,怎么样才能转向J2EE?”,“我看完了Thinking in Java, 可以学习J2EE了么?”。于是就有了写这篇文章的想法,希望能帮助初学者少走一些弯路。也算是对自己几年来学习Java的一个总结吧。
在开始之前有必要再讨论一下J2ME,J2SE,J2EE这些概念。J2ME,The Micro Edition of the Java 2 Platform。主要用于嵌入式Java,如手机,PDA等等。J2SE,Java 2 Platform,Standard Edition,我们通常所说的JDK(Java Development Kit)包含在此,是J2EE的基础。J2EE,Java 2 Platform,Enterprise Edition,就是所谓的企业级Java。这些只是从API级别上的划分,实际上Sun给J2EE的定义是:开发基于组件的多层的企业级应用的规范。也就是为各种不同的技术定义一个Java的规范,使这些不同的技术结合起来,在Java平台上构建强壮的企业级应用。从这一点来看,J2EE这个概念应该是涵盖J2ME,J2SE的。比如一个典型的J2EE应用,网上商店,它支持web方式下订单,也支持手机下订单。显然必须用到J2SE,J2ME。所以也就不存在所谓的从J2SE转向J2EE的问题了,只是后者包含的范围更广而已。

来看看Sun给出的J2EE 相关技术主要分为几大块。
1. Web Service技术
-   Java API for XML Processing (JAXP)
-   Java API for XML Registries (JAXR)
-   Java API for XML-based RPC (JAX-RPC)
-     SOAP with Attachments API for Java (SAAJ)

2. 组件模型技术(Component Model Technologies)
-   Java Servlet
-   JavaServer Pages
-   JavaServer Faces
-   Enterprise JavaBeans
-   Java Message Service
-     J2EE Connector Architecture

3. 管理技术(Management Technologies)
-   J2EE Deployment Specification
-   J2EE Management Specification
-   J2EE Client Provisioning
-     Java Authorization Contract for Containers

4. 其他相关技术(Other J2EE Technologies)
-   JDBC
-   Java Data Objects (JDO)
-   CORBA (Java IDL and Java RMI-IIOP)
-   JavaMail
-   Transactions
如此之多的技术难免使初学者无所适从,望而却步。即使是一位经验丰富的J2EE开发者,又有几个人敢说J2EE相关的技术我都熟练掌握了。不过作为一名普通J2EE应用程序的开发者来说,我们只需要重点学习其中的一部分技术就可以了,对于其他部分只要做到心中有数,哪天需要用到了知道跑哪里去找到资料就行了。以我个人的观点,下面这些技术是一般J2EE应用开发人员所必须熟练掌握的。Java Server Page,Java Servlet,Enterprise JavaBean,JDBC,Transactions。还有JAXP等XML相关技术,Java Message Service,Java Mail,JDO等等是最好应该掌握的。其他Management Technologies,Connector Architecture等等主要是给容器提供商中间件提供商参考的,应用开发者不需要怎么关心,等用到了再去学习也不迟。

语言学习篇
首先是J2SE基础。学习一门新技术,无外乎阅读和实践了。而一本好的参考书对于初学者来说显得格外重要。现在市面上的 Java书籍可以说是铺天盖地,质量也是良莠不齐,令初学者无所适从。所以还是先推荐几本书籍吧。目前对于Java基础知识,大家一般都比较推荐两本书,<<Thinking In Java>>和<< Core Java™ 2, Volume I: Fundamentals >>。第一本书不必多说了,Bruce Eckel的大作,Jolt获奖书籍。内容比较全面,基本涵盖了java语言的方方面面。这本书提供了相当丰富的例子,非常有利于对学习内容的了解。另外书中第一部分对于OO基本书籍的介绍,我觉得对于刚接触OO的人来说帮助会很大。而且此书是Open Source的,可以从作者网上下载http://www.mindview.net/Books/TIJ/而对于习惯于读中文版的学习者来说,侯捷翻译的中文版是不错的选择。要说这本说的缺点可能就是对于初学者来说厚了一点,这也是一些人并不推荐此书作为初学者学习用书的原因吧。后面一本<<Core Java™ 2, Volume I: Fundamentals>>,目前已经是第七版了,单从它出版的次数来看也可以看出此书受欢迎的程度,这本书特点也是讲述比较全面系统,基本上一路啃下来的话Java语言基础应该算过关了。缺点也是太厚了,有点像参考手册,前面部分花了不少篇幅讲Swing和Applet,可能对初学者不是很有用。还有一些像<< Java in a nutshell>>也是比较不错的基础书籍。
学习了基本的语言基础,别忘了最重要也是最有用的资料还是JDK文档。从你学习java的第一天开始JDK文档应该是常备手头了。如果你碰到问题首先想到的是到论坛上去提问而不是查阅Jdk文档,那先别继续往下学习了,学会查JDK文档先。不夸张的说在我们的初学者论坛中60%的问题是光查一下JDK文档就能解决问题的。最新JDK Documentation下载地址http://java.sun.com/j2se/1.4.2/download.html(目前最新版是J2SE5http://java.sun.com/j2se/1.5.0/download.jsp)不能光说不练,同一下载页面把JDK给下载回来。安装完后有一点我想提一下,安装路径下有一个src.zip(有些jdk版本是src.jar),好东西啊---JDK源代码,老是有人在论坛上问哪里有JDK源代码下载,你说东西就放在你家里还到处找。有了这个有些问题就需要在论坛上跟人家争来争去了,翻开源代码瞧一下什么疑问都没有了。几个最重要的命令行工具是
javac:            编译源文件到class文件
java:              运行class
jar:                打包工具。
javadoc:         生成java doc的工具。
对于初学java的人来说,我不推荐使用IDE而直接用文本编辑器,然后用命令行编译运行。这样有利于理解CLASSPATH,PATH这些最基本概念。CLASSPATH是初学者比较容易感觉迷惑的地方。现在的IDE太聪明了,给个名字就给你自动生成java source code,自动编译。可能你运行完了你的第一个Hello World程序,还不知道java和javac是用来做什么的。至于实际的项目开发,一款合适的IDE还是十分重要的,我们稍后再对java开发工具做一些介绍。

J2EE基础和Java语言进阶
学习完语言基础,就可以比较自然地转入J2EE实际技术的学习了。J2EE实在是比较庞杂,而EJB,Servlet,这些核心技术是作为每一个J2EE开发人员所需要掌握的。关于servlet,我比较推荐<<Core Servlet and JSP 2Edition>>和<<More Servlets and Java Server Pages>>,第一本是Sun推荐的Servlet教材。第二本是当年Amazon最畅销Java书籍,五星级书籍。这本书机械工业出版社有中文版叫<<Servlet 与JSP权威指南>>,感觉翻译得还可以,第二版好像还没有看到有中文版。两本书都全面系统地介绍了JSP和Sevlet知识,从web服务器配置,JSP,Servlet基本编程,标记库(Tag Lib),过滤器,事件框架都有很好地描述。提供地例子也比较实用。对于EJB学习,比较著名有两本书,<< Enterprise JavaBeans, 3nd Edition>>和<< Mastering Enterprise Java Beans Third Edition>>,两位作者Richard Monson,Ed Roman都是属于业界重量级人物。而Richard Monson本身就是EJB规范专家组成员。对我来说,两本书难分优劣,第二本书有个好处就是可以免费下载http://www.theserverside.com/books/wiley/masteringEJB/index.tss
还是那句话,不能光说不练,不过J2EE的练习做起来有一点麻烦,应用服务器是不可少的,最好还得准备个轻量级的数据库。下面简单介绍一下这些工具。
web服务器(Servlet Container)方面有。
Tomcat:          http://jakarta.apache.org/tomcat/
Jetty:              http://jetty.mortbay.org/jetty/
应用服务器常用的有,
Jboss:            http://www.jboss.org/products/index
Tomcat,Jetty,Jboss都是Open Source。Weblogic和WebSphere是J2EE服务器中的老大级人物,价格也不菲。不过对于开发者有免费的试用版下载。
如果单单只是学习Servlet,推荐使用Tomcat,它是Sun官方指定的Servlet,JSP规范的参考实现。对初学者最重要的是它使用比较简单,自带文档比较齐全,使用者众多,有什么问题容易在论坛上面得到帮助。如果学习EJB的话,推荐使用Jboss,不仅仅是因为它是Open Source的,主要是配置比较简单,使用方便。比如说对于连接数据库,对于常用的MySQL,Oracle,MS SQL等等都提供了Sample Config文件,直接拿过来做些小改动扔到Deploy目录下就可以用DataSource了,部署J2EE应用也简单,把整个.ear或者.war扔到deploy下就可以了。唯一不方便的地方是从Jboss3.0开始,它的文档开始收费了。但是对于一些基本的配置,在网上还是非常容易找到的,毕竟它太流行了。至于Weblogic,也比较容易使用,不过比起Jboss来个头大了很多,通过强大的管理界面使得一些常用的配置工作变得十分简单。和Jboss比起来它的文档就太多了,简直是有点罗里八嗦,比如要部署一个.ear文件,一般我们也就是直接扔到domain下的applications目录下就会自动deploy了,但是要看它的文档可是长篇大论,容易吓着初学者,以为这又是什么高深的学问。至于WebSphere,个人不推荐初学者使用,相比前俩个Server比较难使,而且狂吃内存。不过在企业级市场这个家伙表现不俗,毕竟是出生于IBM这样的豪门。
数据库方面,目前常见的主要有PostgreSQL,MySQL,Oracle,MS SQL,DB2等等。前面两个是开源数据库,后面几个基本上垄断着大部分的数据库市场。对于初学者用来做做EJB,JDBC的练习,我推荐MySQL,理由还是很简单,开源软件不要钱,个头小使用方面,用户众多文档齐全。下载地址http://www.mysql.com/products/mysql/。PostgreSQL也可以考虑,不过国内使用者远不如MySQL多,所以要在论坛上问起问题来就少方便一些了,下载地址http://www.pervasive-postgres.com/downloads/。至于后面那些比较重量级的数据库,为了做做练习而言就不用考虑了, 咱也花不起这个钱啊。
学习完J2EE的这些具体技术,这个时候进行基本的J2EE开发应该是不成问题了。此时应该考虑提高自己的代码质量了。这里我强烈推荐Martin Fowler的<<Refactoring: Improving the Design of Existing Code >>,这本书不是一本非常实际的书,作者完全是手把手地教你如何提高代码质量,从具体地代码中告诉你什么是代码的Bad Smell,如何去掉这些Bad Smell。不少书评是这么说的,这本书对于初级,中级的读者帮助是立杆见影的。至少就我接触到的几个学习编程不久的程序员,编码质量在短期内都有很大提高。当然重构(Refactoring)这一概念并不只针对Java语言的,它对所有OO语言都是适用的。重构的概念是如此深入人心,以至于今天几乎所有流行的IDE工具都有对重构的支持。这里我还想再推荐一本<<Effective Java>>。从C++过来的程序员都知道<<Effective C++>>在C++领域的地位,至今还流传着这样的趣话,C++程序员分为两种,一种是读过<<Effective C++>>的,另一种是没有读过C++的。虽然这本<<Effective Java>>在Java领域的影响也许没有那么大,但对于Java程序员绝对有相当的指导价值。作者是Sun公司的Joshua Bloch,java Collection framework的设计者。作者站在JDK设计者的角度向你介绍他的Best Practice,应该这样做而不应该那样做,对于JDK中某些API设计的缺陷他也毫不袒护的指出。Java语言之父James Gosling为此书写的前言是这么说的“I sure wish I had had this book ten years ago。 Some might think that I don't need any Java books, but I need this one”。这本书会让你觉得原来你对Java还是有很多东西不了解的。举个例子来说,对象的equals方法,我们认为它很简单,也许你每天都在为你新写的Class重载这个方法,但是你在重载的时候注意过“自反”,“对称”,“传递”这些必须要考虑的因素,你是否同时还小心谨慎的重载了hashcode这个方法?如果没有,建议你要读一下这本书。读完这本书,你会觉得离Java的距离更近了。上面两本书都出过中文版,后面一本<<Effective Java>>还有两个版本的中文版,第一次翻译的比较差一点,后来机械工业出版社又委托潘爱民先生重新翻译了一遍。同一本书在同一个出版社连续被翻译了两次也说明国内出版界对这本书还是比较重视的。
这个阶段,在看书的同时,可以结合着学习一些优秀的开源项目的源代码。这些开源项目的代码风格,注释都是值得借鉴的。实在太懒也别忘了手头上还有个Jdk的源代码。其实也不用刻意去找源代码,在实际的J2EE项目开发中,基本上都会用到一些优秀的开源项目。Framework可能会用到Spring,Struts,Log机制基本上都会JarkartaCommons Log或者Log4j,单元测试会大多会用Junit,结合项目阅读一下其中的一些源代码,既可以提高自己又对项目会有所帮助,说不定因此而得到PM的赏识呢。一举两得,何乐而不为呢。呵呵,有点扯远了。过了初学者阶段,该学会如何找到适合自己的Java书籍了。历经数十载,今天的Java技术已经变的如此之庞杂,我相信即使穷净一个人毕生之精力也不可能把Java所有的相关技术都学通,何况新技术还在层出不穷地推出,3年之前谁会知道Struts会成为Web框架事实上的工业标准。2年之前谁会知道Hibernate会在今天独领风骚。既然已经不能指望一次性把java技术的方方面面都学个通,在实际中也只能是需要什么技术再学习什么技术了。而能否选择一本好的参考书籍带来的就是事半功倍和事倍工半的效果。所以我觉得花点时间放在选择书籍上面还是很值得的,否则你在后面只会花更多的时间。下面我谈谈自己选择书籍的一些经验,不一定正确。首先看作者,像上面提到的那些书的作者,都是业界鼎鼎大名的,选择他们的书一般错不了。大家看的书多了,自己胸中自然也会有一个list,哪些作者是信得过的。二看出版社,计算机书籍方面,Oreilly,Addison-Wesley都是公认比较好的出版社。对于目前比较流行的Java技术,Oreilly的<<XXXX   in   Action>>系列是不错的选择。另外我还会去看看Amazon网站(http://www.amazon.com/)的书评,一般小于3星级的书我都不会考虑。还有一个好去处theserverside,http://www.theserverside.com/的书评,这里的书评比较有趣,往往都有很激烈的争论,里面经常会看到一些名人在发言。我要向所有Java学习者推荐,如果我的收藏夹里面只能存放两个网站,我会选择java.sun和theserverside。在这里你可以了解最新的Java动态,可以学习第一手的Java资料,可以看到Java高手们(里面不乏业界大腕)激烈辩论。
到此阶段,Java Developer的基本功底应该算是打好了吧,往后就是不断学习喽。结束这一段之前,最后再介绍一本书Oreilly的<<Java Threads, Second Edition >>,因为我觉得多线程编程属于Java基本功,每一个想学好Java的人都应该好好掌握。

提高篇
在这个阶段应该从软件架构,Framework层次上来学习了。作为面向对象的圣经<<Design Patterns>>, 这本书是不得不推荐的。不用再多说了,这本在面向对象领域地位完全是属于教父级别的。不管你学习的是什么OO语言,不管你现在是用.Net还是J2EE开发,这本书都是你进阶之路上的必读之书。而<<Core J2EE Patterns>>则专门针对于J2EE来讨论设计模式,书中Sun Java Center的资深设计师描述了J2EE关键技术的模式。最佳实践,设计策略和经过验证的解决方案。对于每一个希望成为J2EE 架构师或者设计师,这本书值得一读。学习设计模式的时候,建议是结合实际的源代码来看,比如看看Junit源代码,你可以看到很多设计模式优雅的实现,作者之一Erich Gamma本身就是<<Design Patterns>>的作者。至于J2EE的设计模式,Sun还开辟了专门的空间http://java.sun.com/blueprints/patterns/,里面有对常用模式的讨论又提供了详细的源代码样例。正如Grady Booch所说,模式对于普遍问题提供了通用的解决方案,利用模式就等于拥有一个强大的专家队伍。如果你还没有学习,现在就开始吧。此外对于面向对象方法论,极限编程的思想也应该有所了解http://www.extremeprogramming.org/。对于J2EE项目的具体实施,Rod Johnson的<<Expert One-on-One J2EE Design and Development (Programmer to Programmer)>>也很有价值,该书以作者丰富的实战经验向我们展示如何用尽可能简单的解决方案构建J2EE 应用,书中作者第一次提出这样的观点,很多时候,J2EE应用完全没有必要用到EJB,对于言必称EJB的广大J2EE开发者来说,怎么说也有点惊世咳俗的味道。当然,作为Servlet和JDO两个专家组的成员,这可不是作者信口胡驺的。今天风靡Java世界的Spring框架最初便是源于此书,而IOC,AOP等概念更是被时下的java开发者挂在嘴边。最后,作为对Java的深入学习,Java技术的各个Specification也有必要一读。

2010-03-24

Emacs配置 gtags+cedet+ecb+doxymacs+session+gdb (Quote)

原帖: http://blog.chinaunix.net/u3/98822/showart_2137437.html

在此之前,我无论是在Windows还是在Linux上开发一直都使用SourceInsight这个软件,对源文件进行编写。
SourceInsight是个非常强大的软件,而且也较易上手。
(注Linux平台开发时使用的是SourceInsight(WIN)+FTP(上下传文件和LinuxPC))

程序仿真和Debug时,原来使用的一直都是使用芯片厂商的专用仿真器,有的仿真器的仿真Debug软件的GUI化很完善,使用方法也很便利。(用鼠标点来点去就行了 + 一些特定命令)


现在在Linux平台上开发软件,同时仿真Debug使用的是GDB(the GNU Project debugger),通过GNU Emacs来调用。
就是因为这个原因,才准备使用Emacs进行对源文件的编写。


单独的Emacs软件的话,虽然也有着除了编辑以外的各种各样的功能,例如Mail,网页浏览等等,但是,仅对于我个人我还是觉得他就是个文本编辑器,因为别的功能我不用,也用不上。而且这个编辑器使用时还非常的费劲,觉不出他比微软的记事本强在哪里。

但是,Emacs支持配置其工作环境,当结合了一些插件后,他就发挥出强大的功能了。
这个强大的功能 = SourceInsight+专用仿真Debug软件之和。
还有就是,他是免费的,FreeDownload的,可自定义的。
微软在努力让我们告别键盘,Linux则建议大家没事别老碰鼠标。

Emacs + gtags + cedet + ecb + doxymas + session + GDB
Emacs:GNU Emacs
gtags:GNU GLOBAL source code tag system
cedet:Collection of Emacs Development Environment Tools
ecb:Emacs Code Browser
doxymacs:Doxygen + Emacs
Doxygen:Source code documentation generator tool
session:Session Management for Emacs
GDB:GNU Project debugger
个人环境:VMware7.0.0 + (Fedora7+Ubuntu9.10)
Fedora7:现在fedora的最高发行版是Fedora12,所以用7的话,如果yum在线安装,包的版本可能较低。
Ubuntu9.10:现在的最新版,apt在线安装的话,包版本较新。
较新不是最新,想最新的话,去各自官网现在src安装吧。

目标源代码:C语言
如果没有现成的代码就下个kernel或gcc或glib的源代码吧。
但是建议写个hello来试运行,因为前者的源代码都太大了,gtags建立tags或htags创建HTML模式源代码和doxygen生成文档等的时候,花费时间极长。
同时也写好hello的configure.ac和Makefile.am吧,能够编译后执行,以便体验功能。



Emacs:GNU Emacs (Ver:23.1)
Emacs:Editor MACroS(宏编辑器)
HomePage:http://www.gnu.org/software/emacs/
功能和热键的学习:
  • http://man.ddvip.com/soft/emacsuserguide/index.html
  • http://www.gnu.org/software/chinese/manual/TUTORIAL.cn
  • 《学习GNU Emacs》(68元)/《GNU Emacs Lisp 编程入门》(38元)
    网上有中文扫描版和英文版的下载,忘了在哪里下的了,搜搜吧。
    扫描版不清晰,例代码根本看不清,建议购入原书。
  • http://man.chinaunix.net/newsoft/Emac/book.html
下载安装:参考上记Homepage。
http://ftp.gnu.org/gnu/emacs/

重点:
 记住个别编辑工作中常用Hotkey。
 知道.emacs文件和<load-path>设置的作用和用法。
 知道LISP语言。(但是不一定要掌握这种语言,抄抄网络上大侠的就行,同时也希望更多的人加入到Lisp行列中)

简单说明:
  • .emacs文件:是Emacs启动时,自动读取用户自定义配置的默认的配置文件
    (手动创建该文件,自定义配置都写在该文件中,LISP语言,"/home/yourname/"路径下)
  • shell命令启动Emacs时,如果用'emacs -q'命令,则不加载任何额外的自定义设置启动Emacs.
  • <load-path>:由'.emacs'文件中编写的相应的自定义配置,调用的关联**.el文件的存放路径。
    (在.emacs中记入该路径时,建议使用绝对路径.这样通过shell命令无论用哪个帐户的ENV启动Emacs时,都能正确读取对应的**.el文件。也就是无视'~'所代替的ENV中的$HOME的值是什么,LISP语言)
    这个<load-path>的文件夹,可以手动创建(推荐),也可以用Emacs软件的默认路径(/usr/local/share/emacs/site-lisp/)。
  • 下记的.emacsLoadpath 就是手动创建的一个文件夹,其中放置一些,准备使用的el文件啦,cedet啦,ecb啦,gtags.el啦等等。

  • 例:
    ;load-path
    (add-to-list 'load-path "/home/yourname/.emacsLoadpath")
个人.emacs文件中的内容:
(仅安装emacs软件后,即可完成的动作,无需安装其他的组件,但需要个别插件,都是参考其他大侠的)
;load-path
(add-to-list 'load-path "/home/yourname/.emacsLoadpath")

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(column-number-mode t);默认显示行号的同时,也显示列号
 '(display-time-mode t);显示当前时间
 '(inhibit-startup-screen t);关闭启动画面
 '(show-paren-mode t);显示与当前光标所在位置的括号匹配的另一个括号
)

(setq visible-bell t);关闭出错时的蜂鸣提示声
(mouse-avoidance-mode'animate);当鼠标箭头与光标相近时,使鼠标箭头自动移开
;(blink-cursor-mode nil);光标不闪烁
(setq-default cursor-type 'bar);光标显示为一竖线
(tool-bar-mode -1);; 不显示emcas的工具栏
(menu-bar-mode -1);; 不显示emcas的菜单栏,按ctrl+鼠标右键仍能调出该菜单
(setq x-select-enable-clipboard t);; 支持emacs和外部程序之间进行粘贴
(fset 'yes-or-no-p 'y-or-n-p);以 'y/n'字样代替原默认的'yes/no'字样
(setq frame-title-format "%b@emacs");在最上方的标题栏显示当前buffer的名字
(setq make-backup-files nil);关闭自动备份功能
(setq auto-save-mode nil);关闭自动保存模式
(setq auto-save-default nil);不生成名为#filename# 的临时文件
(setq require-final-newline t);; 自动的在当前的buffer文件的最后加一个空行
(global-set-key "\r" 'align-newline-and-indent);;自动缩进<C-j>变为<Enter>
(setq echo-keystrokes 0.1);; 尽快显示按键序列提示
(global-font-lock-mode t);; 语法高亮
;; 用来显示当前光标在哪个函数
;(require 'which-func)
(which-func-mode 1)
(setq which-func-unknown "unknown")
;; 用M-x执行某个命令的时候,在输入的同时给出可选的命令名提示
(icomplete-mode 1)
(define-key minibuffer-local-completion-map (kbd "SPC") 'minibuffer-complete-word)

(global-hl-line-mode 1);;高亮当前行

;;;;;;;;;;;;;;;;;启动时最大化;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'maxframe)
(add-hook 'window-setup-hook 'maximize-frame t)
;下载maxframe.el并放置在<load-path>中
;http://emacsblog.org/2007/02/22/maximize-on-startup-part-2/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;web方式显示行号;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;(set-scroll-bar-mode 'right);滚动条在右侧
(set-scroll-bar-mode nil)   ; 不显示滚动条, even in x-window system (recommended)
(require 'wb-line-number)
(wb-line-number-toggle)
;下载wb-line-number.el并放置在<load-path>中
;http://homepage1.nifty.com/blankspace/emacs/elisp.html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;cc-mode;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(add-to-list 'load-path "/home/lb/.emacsLoadpath/cc-mode-5.31.3")
(require 'cc-mode)
(c-set-offset 'inline-open 0)
(c-set-offset 'friend '-)
(c-set-offset 'substatement-open 0)
;http://cc-mode.sourceforge.net/
;http://www.kklinux.com/html/linuxwangluojishu/linuxxitongguanliyuan/200902/28-3728.html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(set-face-background 'default "gainsboro");背景设定
;颜色参考value:X界面菜单栏中[edit]->[text properties]->[Display colors]

;未完其他内容在本文的后续部分介绍
;session
;global(GNU GLOBAL source code tag system)
;##cedet(Collection of Emacs Development Environment Tools)
;##ecb(Emacs Code Browser)
;##doxymacs
;##compile
;##GDB







gtags:GNU GLOBAL source code tag system (Ver:5.7.7)
HP:http://www.gnu.org/software/global/
看看Tutorial(指导)中的Overview就知道,通过gtags能够建立软件工程项目。
这就说明,Emacs+gtags的组合,就可以是能够创建工程项目的编辑软件了。
(基本等于sourceinsight,如果在加上cedet和ecb就更完美了)

还有很多人不用gtags而用的是cscope,我个人没用过cscope,大家可尝试使用。

支持的基本环境
  • Shell command line
  • Bash shell
  • Vi editor (Nvi, Elvis, vim)
  • Less viewer
  • Emacs editor (Emacs, Mule, Xemacs)
  • Web browser
  • Doxygen documentation system
基本用法:
①下载安装gtags后,在源文件的目录中,键入"gtags -v"命令,就会生成相应文件。
[xx@localhost sys]$ gtags -v
例:source code(/usr/src/sys)       123MB

    GPATH                             1MB `GPATH'  path name database
    GTAGS                            26MB `GTAGS'  definition database
    GRTAGS                           22MB `GRTAGS' reference database
    GSYMS                            23MB `GSYMS'  symbol database
    -------------------------------------
    total of tag files               72MB
gtags的其他options的含义,请参考man手册。
(辅助:http://blog.chinaunix.net/u3/98822/showart_2129660.html)

②下载安装gtags后,会有名为gtags.el的文件,注意看安装时make install的list,即可找到gtags.el的安装位置(默认目录是:/usr/local/share/gtags/)。
gtags.el的路径设置为Emacs的<load-path>。
例:
`$HOME/.emacs'文件中加入下记代码例。
(setq load-path (cons "/home/owner/global" load-path));gtags.el load-path
(autoload 'gtags-mode "gtags" "" t);gtags-mode is true
从此启动Emacs后,及在<load-path>中load了gtags.el文件,并将gtags-mode设置为真(等同于每次启动后,都在Emacs中的M-x,手动键入gtags-mode)。

个人.emacs文件中的内容:
;##global(GNU GLOBAL source code tag system);;;;;;;;;;;;;;;;;;;;;
;to use global from Emacs, you need to load the `gtags.el' and execute gtags-mode function in it.
;you need to add it to load-path. for `gtags.el'file.
;(add-to-list 'load-path "<path to gtags.el>");已经在之前的代码中load完了
(autoload 'gtags-mode "gtags" "" t);;start Emacs and execute gtags-mode function. 
(setq c-mode-hook
      '(lambda ()
     (gtags-mode 1)));get into gtags-mode whenever you get into c-mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

③当源程序的源文件的内容变化了(导致行号变化),或追加删除了源文件等等。只要有改变,就要更新gtags生成的那4个文件。
例:source code(/usr/src/sys)
[xx@localhost sys]$ gtags -vi
or
[xx@localhost sys]$ global -u (推荐)

详细用法:
直接参考gtags的英文的Tutorial(指导)。
有很多章节可以跳过,因为那是global在别的编辑软件中的应用方法的说明。(例如shell,vi等)
Extended Emacs using GLOBAL 章节还是要必看的。

看了gtags的英文的Tutorial(指导)后,发现htags也蛮有用的。

注:gtags-parser和gtags-cscope没搞明白,哪位大侠能帮帮忙解说一下,或发个说明链接什么的。
gtags会调用gtags-parser和gtags-cscope对目标代码,进些分析,目的为生成源码的tags。?

gtags完成的基本动作(具备键盘的快捷键):
  • 鼠标滚轮键(下按):根据上下文,找到"函数或常量或变量"的"定义或调用"的位置。
  • 鼠标右键:返回。(就是按完滚轮键后,想返回到原来刚才的位置的话,就按右键)
  • 光标+Enter:当按滚轮键后,出现的调用位置的结果为多个时,将光标调整到相应行,按下回车即可跳转到对应位置。
    (如图,名为_exit_ts的函数在整个项目中,有多个位置调用他)




※※※※※ 先去试试上记内容吧!如果不行,往下进行也没什么意义。※※※※※




CEDET:Collection of Emacs Development Environment Tools Ver:1.0pre6


HP:http://cedet.sourceforge.net/
安装:http://cedet.sourceforge.net/setup.shtml  (切记看看下载后的INSTALL文件,需要make编译)
没有CEDET的话,后续的ecb不能安装使用。

这个组件,安装简单,使用便利。(望大家阅读文档,了解他的各种功能)

我个人常用的两个功能是,speedbar和smart code completion(自动补全)

个人.emacs文件中的内容:
;##cedet(Collection of Emacs Development Environment Tools);;;;;;
(load-file "/home/yourname/.emacsLoadpath/cedet/common/cedet.el")
;读取子目录中的特别的 .el 文件
(global-ede-mode 1); Enable the Project management system
(semantic-load-enable-code-helpers); Enable prototype help and smart completion
(global-srecode-minor-mode 1); Enable template insertion menu
(global-set-key [(f4)] 'speedbar-get-focus);speedbar快捷键[F4]
(define-key c-mode-base-map [(control tab)] 'semantic-ia-complete-symbol-menu);自动补全Ctrl+tab
;(control tab)(meta ?/)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

speedbar插图




smart code completion(自动补全)插图:
就是当输入一个已有的函数名或变量常量名的时候,输到一半时按快捷键自动补全剩余的字母。
我自定义的该功能的快捷键是<Ctrl> + <Tab>,也有设成<Meta> + / 的,根据个人习惯,自行定义。



来自官网的插图:






注:当前发布的最新版的CEDET Ver:1.0pre6,在使用的时候发现了一个问题。
就是在执行自动补全功能,索引tags时,到LL/confname.h的10%时,Linux系统会down机
这个不幸的事情在我的电脑上发生了。

解决方法(转载):http://hi.baidu.com/susdisk/blog/item/dceeded0278c7d85a1ec9c01.html
----转载------
cedet索引LL/confname.h时emacs挂掉解决方案
2009年12月06日 星期日 19:11
    使用cedet1.0pre6时一直有这个问题,一旦看一个新工程时,semantic进行索引tags,到LL/confname.h的10%时就会down掉,没有响应。在mail-list里面已经有讨论(http://www.opensource-archive.org/showthread.php?t=101444),解决方案也很简单,不用1.0pre6,使用cvs版本,如果还使用了ecb,则在重新换cvs版本后还要重编译ecb,否则emacs是load error的。cedet的cvs版本获取地址:
(shell命令)

cvs -z3 -d:pserver:anonymous@cedet.cvs.sourceforge.net:/cvsroot/cedet co -P cedet
    现在好了,emacs不会在看一个新工程时就挂掉了,而且听说cvs版本的补全功能更完备一些。
------------




ECB:Emacs Code Browser Ver:ecb-2.40

HP:http://ecb.sourceforge.net/
安装:http://ecb.sourceforge.net/docs/Installation.html#Installation
其中
Requirements内容中的1和2还是必要的,3和4对于我来说用不上(我的环境非XEmacs,目标代码也不是Java)

辅助:
http://blog.csdn.net/intrepyd/archive/2009/07/09/4333893.aspx
  • 其中在上记"辅助"的blog中设置[鼠标支持]的时候,fedora使用'鼠标滚轮下按'的方式选择和设置。
  • 关闭ecb的"每日提醒"和info和upgrandoption界面,请参考下记.emacs文件中的代码。

疑问:ecb中的
Methods窗口为什么有的时候是空的,什么都没有?
有的时候能够正确表示当前源文件中定义的函数/类型/成员列表呢?
难道是当前的文件定义的数量太多?
貌视保存一下当前文件,就能正确表示了。(待反复确认)
个人.emacs文件中的内容:
;##ecb(Emacs Code Browser);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(add-to-list 'load-path
                     "/home/lb/.emacsLoadpath/ecb")
(load-file "/home/lb/.emacsLoadpath/ecb/ecb.el")
(require 'ecb)
(require 'ecb-autoloads)
;(setq ecb-auto-activate t);自动启动ecb
(setq ecb-auto-activate t;自动启动ecb
ecb-tip-of-the-day nil;不显示每日提醒
inhibit-startup-message t;不知道什么意思,望各位指导
ecb-auto-compatibility-check nil;
ecb-version-check nil;
)
(global-set-key [f8] 'ecb-activate) ;;定义F8键为激活ecb
(global-set-key [f7] 'ecb-deactivate) ;;定义F7为停止ecb
;;;; 各窗口间切换
(global-set-key [M-left] 'windmove-left)
(global-set-key [M-right] 'windmove-right)
(global-set-key [M-up] 'windmove-up)
(global-set-key [M-down] 'windmove-down)
;;;; 使某一ecb窗口最大化
(define-key global-map "\C-c1" 'ecb-maximize-window-directories)
(define-key global-map "\C-c2" 'ecb-maximize-window-sources)
(define-key global-map "\C-c3" 'ecb-maximize-window-methods)
(define-key global-map "\C-c4" 'ecb-maximize-window-history)
;;;; 恢复原始窗口布局
(define-key global-map "\C-c`" 'ecb-restore-default-window-sizes)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ecb界面插图



ecb的Method窗口最大化




官网的各种插图
http://ecb.sourceforge.net/screenshots/index.html

※※※※※ 到这里是不是觉得已经可以基本完全=SourceInsight了呢?※※※※※
※※※※※   去试试上记内容吧!如果不行,往下进行还是没啥意思。   ※※※※※




doxymacs:Doxygen + {X}Emacs Ver:1.8.0

文档文档文档,文档是写给谁的?写给别人看的?写给领导看的?
我觉得文档是写给你自己的,为了备忘、整理思路和确认检查。(例如本贴,望大家都来留下一笔吧)
但是写文档,好像挺费劲呀。尤其是coding完成/功能实现之后,就更不愿意回过头来写已经完成的功能的设计文档了。
其实设计文档都应该在实际coding之前完成,而不是,之后补写。

那么,就用Doxygen根据代码来生成文档吧。不过生成的文档,只能当做该模块的外部规格文档。详细设计文档可以在此基础上修改做成。使得文档的做成变得简单方便了。

那Doxymacs和Doxygen是什么关系呢?

Doxygen是根据code生成文档的工具。
主要是根据code中的注释内容生成文档,这个注释是要按照Doxygen工具中约定的写法格式才能够正确的生成文档。
他可以使你养成写注释的习惯,写好注释的习惯。
同时你做成的代码,后期可维护能力也大幅提高。
如果你看到几千行的code一行注释都没有,会不会有种想吐的感觉呢?

Doxymacs则是结合在Emacs中的插件。
通过快捷键,自动完成符合Doxygen中要求的注释格式。
当然注释的具体内容是要由设计者自行添加的。

Doxymacs HP: http://doxymacs.sourceforge.net/
辅助:http://blog.chinaunix.net/u3/98822/showart_2137605.html

个人.emacs文件中的内容:
;##doxymacs;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(add-to-list 'load-path "/home/lb/share/emacs/site-lisp/");读取安装el的路径
(require 'doxymacs) ;; 启动doxymacs
;;注释高亮,针对C和C++程序
(defun my-doxymacs-font-lock-hook ()
  (if (or (eq major-mode 'c-mode) (eq major-mode 'c++-mode))
      (doxymacs-font-lock)))
(add-hook 'font-lock-mode-hook 'my-doxymacs-font-lock-hook)
(doxymacs-mode);doxymacs-mode常true
(global-set-key [(f6)] 'doxymacs-mode);doxymacs-mode快捷键[F6]
;(add-hook 'c-mode-common-hook 'doxymacs-mode) ;; 启动doxymacs-mode
;(add-hook 'c++-mode-common-hook 'doxymacs-mode) ;; 启动doxymacs-mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




Doxygen:Source code documentation generator tool Ver:1.6.2

HP:http://www.stack.nl/~dimitri/doxygen/index.html
安装方法和使用方法,看看Manual吧。

辅助:
http://myfaq.com.cn/2005September/2005-09-13/200335.html
http://blog.csdn.net/SigmaSys/
http://blog.csdn.net/nightelve/archive/2008/05/31/2498047.aspx
http://blog.chinaunix.net/u3/98822/showart_2136698.html

生成的文档的格式类型:
HTML
LaTeX
RTF
XML
Man Page

Doxygen会根据生成的configuration文件中的设定,来生成对应的文档。
Doxywizard可以更方便的设置这个configuration设定文件(GUI化)。
要么就用text模式来自行设置configuration文件,这个文件有点像Makefile文件的感觉。

看完Getting started章节后,即可生成你的第一份文档了。
不过详细的内容,还是请仔细阅读Manual。


关于Doxywizard:
使用源代码安装,没能安装上。异常的messages忘了是什么就不贴出来了。
终于装上了,http://blog.chinaunix.net/u3/98822/showart.php?id=2154045

但是,由于使用的是fedora系统,所以通过yum找到了Doxywizard 1.5.5,并安装成功。(其他Linux使用apt命令)

那么就将Doxywizard 1.5.5的贴图发出来让大家看看
(不知和doyxgen 1.6.2一起使用会有什么异常,也许是新的个别选项无法设置吧)
(建议大家源码安装和doyxgen 1.6.2匹配的Doxywizard)
哦,对了这个Doxywizard的启动是通过shell命令方式启动的,不是点击图标的形式。





插一张,doxygen生成的method图,以便大家参考,当然还生成各个函数的独立的图等等




有时通过method图,也能发现软件潜在的风险,如下图main函数的红色箭头部分






对于软件的分析,仅用上面的method类状态迁移图是不够的,尤其是对别人的代码进行分析。
再生成流程图吧。(autoflowchart or Crystal REVS)(非doxygen)
http://blog.chinaunix.net/u3/98822/showart_2145179.html






关于Fedora的yum:
yum是在线更新fedora的重要工具之一:Yellow dog Updater

yumex是yum命令的图形化前端可选组件。(yum extender)
HP:http://www.yum-extender.org/blog/

安装时,可使用shell命令:$ yum install yumex
网上相关说明很多。(yum命令的用法)

插图:






※※※※※文档已经能生成了,后面内容都是辅助内容了(但用处很大,提高效率)※※※※※




session:Session Management for Emacs Ver:session-2.2a

HP:http://emacs-session.sourceforge.net/


下记转载:

来自http://man.ddvip.com/soft/emacszh/x373.html
----------------------
5.2 session
5.2.1 session简介

session扩展包可以使Emacs保存每次编辑的一些历史记录, 这样, 在下次打开Emacs时就可以使用上下键在以前输入的信息中选取, 十分方便实用.
5.2.2 session的使用

session的使用十分简单, 只需要在~/.emacs中加入如下几句就可 以了:

(add-to-list 'load-path "<path to session>")
(require 'session)
(add-hook 'after-init-hook 'session-initialize)
----------------------

来自http://www.smth.edu.cn/pc/pccon.php?id=6063&nid=143960
----------------------
;;记录所有操作
(require 'session)
(add-hook 'after-init-hook 'session-initialize)

;;记录和恢复屏幕
(load "desktop")
(desktop-load-default)
(desktop-read)
;;desktop自动存盘模式
,23需要加
(desktop-save-mode 1)
----------------------

在GNU Emacs的Manual中有详细的说明。
找找吧。google下"Emacs session"就有。



注:在使用session的时候,每次启动都有个提示信息,需要按'y'后,完成启动。
---------
Please answer y or n. Warning: desktop file appears to be in use by PID ????.
Using it may cause conflicts. Use it anyway? (y or n) "
---------
参考了下记帖子后,解决。
(即去掉(desktop-read)项,我还真说不清这项是干什么用的,没读手册和帮助呀)
https://bugs.launchpad.net/ubuntu/+source/emacs-snapshot/+bug/163342

个人.emacs文件中的内容:
;;;;;;;;;;;session;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;??????
;(add-to-list 'load-path "<path to session>")
(require 'session)
(add-hook 'after-init-hook 'session-initialize);启动时初始化session

;;记录和恢复屏幕
(load "desktop")
(desktop-load-default)
;(desktop-read)
;;desktop自动存盘模式,23需要加
(desktop-save-mode 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;






GDB:The GNU Project Debugger  Ver:7.0.1

HP:http://www.gnu.org/software/gdb/

GDB的使用方法就不多说了,网上找找吧。

这里仅介绍他的功能之一:gdb-many-windows

个人.emacs文件中的内容:
;##GDB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(global-set-key [(f5)] 'gdb-many-windows);gdb-many-windows快捷键[F5]
(setq gdb-use-separate-io-buffer t) ; 不需要"IO buffer"时,则设为nil
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

--------------
注:装完GDB 7.0.1 后,GDB不能启动。
错误提示是:
gdb: error while loading shared libraries: libiconv.so.2: cannot open shared object file: No such file or directory

解决方法是(网摘):
在/etc/bashrc 或 ~/.bashrc 或 ~/.bash_profile 等等,位置加入下记:

export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
--------------
(是不是GDB安装的路径错了呢!?)


GDB模式启动后,下面的6个buffer将被显示:

+----------------------------------------------------------------------+
|                               GDB Toolbar                            |
+-----------------------------------+----------------------------------+
| GUD buffer (I/O of GDB)           | Locals buffer                    |
|                                   |                                  |
|                                   |                                  |
|                                   |                                  |
+-----------------------------------+----------------------------------+
| Source buffer                     | I/O buffer (of debugged program) |
|                                   | (comint-mode)                    |
|                                   |                                  |
|                                   |                                  |
|                                   |                                  |
|                                   |                                  |
|                                   |                                  |
|                                   |                                  |
+-----------------------------------+----------------------------------+
| Stack buffer                      | Breakpoints buffer               |
| RET      gdb-frames-select        | SPC    gdb-toggle-breakpoint     |
|                                   | RET    gdb-goto-breakpoint       |
|                                   | D      gdb-delete-breakpoint     |
+-----------------------------------+----------------------------------+

各buffer的含义如下:
  • GDB Toolbar        - GDB 操作Toolbar
  • GUD buffer         - 执行操作的buffer
  • Locals buffer      - 本地变量名和值的表示buffer
  • Source buffer      - 表示sourcecode的buffer
  • IO/ buffer         - 表示程序的输入输出的buffer
  • Stack buffer       - 运行挺值的时候,调用关系的表示buffer
  • Breakpoints buffer - breakpoints断点的表示buffer
buffer崩溃的时候、通过'M-x gdb-restore-windows'返回原状态。



gdb-many-windows插图







其他内容:
1.compile
GNU Emacs中有个功能叫compile。
当编辑完源代码后,<M-x>中输入compile,会跳转执行make -k。
我记得 -k 的意思是,在遇见错误的时候,不停止,继续编译。之后,会有发现错误的个数的表示。
通过快捷键可直接跳转到每个错误的源代码,方便修正错误。

看了《学习GNU Emacs》一书之后,就知道了。

个人.emacs文件中的内容:
;##compile;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-key c-mode-base-map [(f9)] 'compile);emacs的compile命令快捷键F9
(setq compile-command "make");默认的make -k命令,变为make命令
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


2.buffer切换
当打开多个buffer的时候,相互之间切换,不要用'M-x o'了。
这种有序的切换方式,太慢了,影响工作效率。

请使用'shift + 光标':就是shift + (↑ ↓ ← →)
.emacs文件中追加

;; move window (Shift + cursor)
(windmove-default-keybindings)
(setq windmove-wrap-around t)
;;;;;;;;





续集:
  • Emacs Lisp auto-complie自动编译 + 自动更新gtags
    http://blog.chinaunix.net/u3/98822/showart.php?id=2151084
  • Emacs显示行号。
    http://blog.chinaunix.net/u3/98822/showart.php?id=2151091
  • 去掉ecb启动时的'first steps'帮助信息的显示
    http://blog.chinaunix.net/u3/98822/showart.php?id=2151592
  • 待续


更多 : 个人 .emacs 文件备份http://blog.chinaunix.net/u3/98822/showart.php?id=2144154



写完了,欢迎指正。