lyjinger读书笔记之:Advanced Linux Programming
原书信息:
名称:Advanced Linux Programming
版本:FIRST EDITION: June, 2001
作者:Mark Mitchell, Jeffrey Oldham, and Alex Samuel
读书笔记:
g++ -c -D NDEBUG=3 reciprocal.cpp
gcc -c -DNDEBUG test.c
gdb调试
gdb [exe]
run [arglist]
continue
where
up [level]
print
list [file]:[start,stop]
break [where]
step #step in
next #step over
man page
(1)user commands
(2)system calls
(3)standard library functions
(8)system/administrative commands
whatis [name]
man -k [keyword]
info
Among the most useful Info documents are these:
gcc—The gcc compiler
libc—The GNU C library, including many system calls
gdb—The GNU debugger
emacs—The Emacs text editor
info—The Info system itself
the system header files dir:
/usr/include
/usr/include/sys
a lot of the nitty-gritty details of how the system calls work are reflected in header files dir:
/usr/include/bits
/usr/include/asm
/usr/include/linux
GNU coding standards
info "(standards)User Interfaces"
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
You invoke the getopt_long function, passing it the argc and argv arguments to main,
the character string describing short options, and the array of struct option elements
describing the long options.
Each time you call getopt_long, it parses a single option, returning the shortoption
letter for that option, or –1 if no more options are found.
Typically, you’ll call getopt_long in a loop, to process all the options the user has
specified, and you’ll handle the specific options in a switch statement.
If getopt_long encounters an invalid option (an option that you didn’t specify as
a valid short or long option), it prints an error message and returns the character
? (a question mark). Most programs will exit in response to this, possibly after
displaying usage information.
When handling an option that takes an argument, the global variable optarg
points to the text of that argument.
After getopt_long has finished parsing all the options, the global variable optind
contains the index (into argv) of the first nonoption argument.
stdout是带缓冲的,而stderr是不带缓冲的,fflush(stdout)
extern char** environ;
char *getenv(const char *name);
int mkstemp(char *template);
Temporary files created with mkstemp are not deleted automatically. It’s up to you
to remove the temporary file when it’s no longer needed.
If the temporary file is for internal use
only and won’t be handed to another program, it’s a good idea to call unlink on the
temporary file immediately.
FILE *tmpfile (void);
The temporary file is already unlinked, as in
the previous example, so it is deleted automatically when the file pointer is closed
(with fclose) or when the program terminates.
GNU/Linux provides several other functions for generating temporary files and temporary
filenames, including mktemp, tmpnam, and tempnam. Don’t use these functions,
though, because they suffer from the reliability and security problems already mentioned.
With NDEBUG set, appearances of the assert macro will be preprocessed away. It’s a
good idea to do this only when necessary for performance reasons, though, and only
with performance-critical source files.
Use assert for internal runtime checks only
Some good places to use assert are these:
Check against null pointers, for instance, as invalid function arguments
Check conditions on function parameter values.
#include <errno.h>
#include <string.h>
extern int errno;
char *strerror(int errnum);
void perror(const char *s);
If you decide to return from the middle of a function, it’s important to make sure
that any resources successfully allocated previously in the function are first deallocated.
These resources can include memory, file descriptors, file pointers, temporary files,
synchronization objects, and so on.
Linux cleans up allocated memory, open files, and most other resources when a program
terminates, so it’s not necessary to deallocate buffers and close files before calling
exit.
Because the linker searches the archive when it is encountered on the command
line, it usually makes sense to put archives at the end of the command line.
gcc -o app -L. -ltest app.o #error
gcc -o app app.o -L. –ltest #right
When the program is actually run, the system searches for the shared
library and loads it.The system searches only /lib and /usr/lib, by default. If a shared
library that is linked into your program is installed outside those directories, it will not
be found, and the system will refuse to run the program.
One solution to this problem is to use the -Wl,-rpath option when linking the
program. Suppose that you use this:
% gcc -o app app.o -L. -ltest -Wl,-rpath,/usr/local/lib
Another solution to this problem is to set the LD_LIBRARY_PATH environment
variable when running the program. Like the PATH environment variable,
LD_LIBRARY_PATH is a colon-separated list of directories. For example, if
LD_LIBRARY_PATH is /usr/local/lib:/opt/lib, then /usr/local/lib and /opt/lib
will be searched before the standard /lib and /usr/lib directories.You should also
note that if you have LD_LIBRARY_PATH, the linker will search the directories given
there in addition to the directories given with the -L option when it is building an
executable.
The standard C library math functions are not included in libc;
instead, they’re in a separate library, libm, which you need to specify explicitly.
Pros and Cons:
One major advantage of a shared library is that it saves space on the system where
the program is installed.
A related advantage to shared libraries is that users can upgrade the libraries without
upgrading all the programs that depend on them.
The fact that an upgrade to a shared library affects all programs that depend on it
can be a disadvantage.
If you’re not going to be able to install your libraries in /lib or /usr/lib, you
should definitely think twice about using a shared library.
void *dlopen(const char *filename, int flag);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
pid_t getpid(void);
pid_t getppid(void);
ps -e -o pid,ppid,cmd,stat,time,tty
int system(const char *string);
In fact, system creates a subprocess running the standard Bourne shell (/bin/sh)
and hands the command to that shell for execution.
The system function returns the exit status of the shell command. If the shell itself
cannot be run, system returns 127; if another error occurs, system returns –1.
it’s preferable to use the fork and exec method for
creating processes.
To spawn a new process, you first use fork to
make a copy of the current process.Then you use exec to transform one of these
processes into an instance of the program you want to spawn.
pid_t fork(void);
Within the exec family, there are functions that vary slightly in their capabilities
and how they are called.
Functions that contain the letter p in their names (execvp and execlp) accept a
program name and search for a program by that name in the current execution
path; functions that don’t contain the p must be given the full path of the program
to be executed.
Functions that contain the letter v in their names (execv, execvp, and execve)
accept the argument list for the new program as a NULL-terminated array of
pointers to strings. Functions that contain the letter l (execl, execlp, and
execle) accept the argument list using the C language’s varargs mechanism.
Functions that contain the letter e in their names (execve and execle) accept an
additional argument, an array of environment variables.The argument should be
a NULL-terminated array of pointers to character strings. Each character string
should be of the form “VARIABLE=value”.
Because exec replaces the calling program with another one, it never returns unless an
error occurs.
When you use an exec function in your programs, you should pass the name of the function
as the first element of the argument list.
To change the niceness of a running process programmatically, use the nice function.
Its argument is an increment value, which is added to the niceness value of the
process that calls it. Remember that a positive value raises the niceness value and thus
reduces the process’s execution priority.
Note that only a process with root privilege can run a process with a negative niceness
value or reduce the niceness value of a running process.
The SIGTERM signal asks a process to terminate; the process may
ignore the request by masking or ignoring the signal.The SIGKILL signal always kills the process
immediately because the process may not mask or ignore SIGKILL.
The SIGHUP signal is sometimes used for this purpose as well, commonly to wake up
an idling program or cause a program to reread its configuration files.
Because signals are asynchronous, the main program may be in a very fragile state
when a signal is processed and thus while a signal handler function executes.
Therefore, you should avoid performing any I/O operations or calling most library
and system functions from signal handlers.
int sigaction(int signum, const struct sigaction *act, struct sigaction
*oldact);
Linux guarantees that assignments to variables of this type(sig_atomic_t) are performed
in a single instruction and therefore cannot be interrupted midway. In Linux,
sig_atomic_t is an ordinary int; in fact, assignments to integer types the size of int or
smaller, or to pointers, are atomic. If you want to write a program that’s portable to
any standard UNIX system, though, use sig_atomic_t for these global variables.
SIGBUS, SIGSEGV, and SIGFPE cause the process to terminate.
To send a signal from a program, use the kill function.
int kill(pid_t pid, int sig);
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
the WEXITSTATUS macro extracts the child process’s exit code.
You can use the WIFEXITED macro to determine from a child process’s exit status
whether that process exited normally (via the exit function or returning from main)
or died from an unhandled signal. In the latter case, use the WTERMSIG macro to extract
from its exit status the signal number by which it died.
A zombie process is a process that has terminated but has not been cleaned up yet.
When a program exits, its children are inherited by a special process, The
init process automatically cleans up any zombie child processes that it inherits.
When a child process terminates, Linux sends the parent process the SIGCHLD signal.
Thus, an easy way to clean up child processes is by handling SIGCHLD.
Make sure that any data you pass to a thread by reference is
not deallocated, even by a different thread, until you’re sure that the thread is done with
it.This is true both for local variables, which are deallocated when they go out of
scope, and for heap-allocated variables, which you deallocate by calling free (or using
delete in C++).
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void *
(*start_routine)(void *), void * arg);
int pthread_join(pthread_t th, void **thread_return);
if (!pthread_equal (pthread_self (), other_thread))
pthread_join (other_thread, NULL);
To specify customized thread attributes, you must follow these steps:
1. Create a pthread_attr_t object.The easiest way is simply to declare an automatic
variable of this type.
2. Call pthread_attr_init, passing a pointer to this object.This initializes the
attributes to their default values.
3. Modify the attribute object to contain the desired attribute values.
4. Pass a pointer to the attribute object when calling pthread_create.
5. Call pthread_attr_destroy to release the attribute object.The pthread_attr_t
variable itself is not deallocated; it may be reinitialized with pthread_attr_init.
For most GNU/Linux application programming tasks, only one thread attribute is
typically of interest.This attribute is the thread's detach state.
(joinable thread (the default) or detached thread)
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
#pass PTHREAD_CREATE_DETACHED as the second argument
int pthread_attr_destroy(pthread_attr_t *attr);
To cancel a thread, call pthread_cancel, passing the thread ID of the thread to be
canceled. A canceled thread may later be joined; in fact, you should join a canceled
thread to free up its resources.
A thread may be in one of three states with regard to thread cancellation.
The thread may be asynchronously cancelable.The thread may be canceled at any
point in its execution.
The thread may be synchronously cancelable.The thread may be canceled, but not
at just any point in its execution. Instead, cancellation requests are queued, and
the thread is canceled only when it reaches specific points in its execution.
A thread may be uncancelable. Attempts to cancel the thread are quietly ignored.
int pthread_setcanceltype(int type, int *oldtype);
int pthread_setcancelstate(int state, int *oldstate);
int old_cancel_state;
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cancel_state);
/*do some work...*/
pthread_setcancelstate (old_cancel_state, NULL);
GNU/Linux supports this by providing each thread with a
thread-specific data area.The variables stored in this area are duplicated for each thread,
and each thread may modify its copy of a variable without affecting other threads.
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void
*));
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *pointer);
void * pthread_getspecific(pthread_key_t key);
线程同步机制:Mutexes, Semaphores for Threads, Condition Variables
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex-
attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t mutex;
pthread_mutex_init (&mutex, NULL);
等同于:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Three kinds of mutexes exist:
Locking a fast mutex (the default kind) will cause a deadlock to occur.An
attempt to lock the mutex blocks until the mutex is unlocked. But because the
thread that locked the mutex is blocked on the same mutex, the lock cannot
ever be released.
Locking a recursive mutex does not cause a deadlock. A recursive mutex may
safely be locked many times by the same thread.The mutex remembers how
many times pthread_mutex_lock was called on it by the thread that holds the
lock; that thread must make the same number of calls to pthread_mutex_unlock
before the mutex is actually unlocked and another thread is allowed to lock it.
GNU/Linux will detect and flag a double lock on an error-checking mutex that
would otherwise cause a deadlock.The second consecutive call to
pthread_mutex_lock returns the failure code EDEADLK.
Each semaphore has a counter value, which is a non-negative integer.A semaphore
supports two basic operations:
A wait operation decrements the value of the semaphore by 1. If the value is
already zero, the operation blocks until the value of the semaphore becomes
positive (due to the action of some other thread).When the semaphore’s value
becomes positive, it is decremented by 1 and the wait operation returns.
A post operation increments the value of the semaphore by 1. If the semaphore
was previously zero and other threads are blocked in a wait operation on that
semaphore, one of those threads is unblocked and its wait operation completes
(which brings the semaphore’s value back to zero).
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t * sem);
int sem_trywait(sem_t * sem);
int sem_post(sem_t * sem);
int sem_getvalue(sem_t * sem, int * sval);
int sem_destroy(sem_t * sem);
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t
*cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_destroy(pthread_cond_t *cond);
Whenever your program performs an action that may change the sense of the condition
you’re protecting with the condition variable, it should perform these steps.
1. Lock the mutex accompanying the condition variable.
2. Take the action that may change the sense of the condition (in our example, set
the flag).
3. Signal or broadcast the condition variable, depending on the desired behavior.
4. Unlock the mutex accompanying the condition variable.
on GNU/Linux, threads are implemented as processes.Whenever you call pthread_create to create a
new thread, Linux creates a new process that runs that thread. However, this process is
not the same as a process you would create with fork; in particular, it shares the same
address space and resources as the original process rather than receiving copies.
The manager thread is created the first time a program calls pthread_create to create a new thread.
Here are some guidelines to help you decide which concurrency model best suits your program:
All threads in a program must run the same executable.A child process, on the
other hand, may run a different executable by calling an exec function.
An errant thread can harm other threads in the same process because threads
share the same virtual memory space and other resources. For instance, a wild
memory write through an uninitialized pointer in one thread can corrupt
memory visible to another thread.
An errant process, on the other hand, cannot do so because each process has a
copy of the program’s memory space.
Copying memory for a new process adds an additional performance overhead
relative to creating a new thread. However, the copy is performed only when
the memory is changed, so the penalty is minimal if the child process only reads
memory.
Threads should be used for programs that need fine-grained parallelism. For
example, if a problem can be broken into multiple, nearly identical tasks, threads
may be a good choice. Processes should be used for programs that need coarser
parallelism.
Sharing data among threads is trivial because threads share the same memory.
(However, great care must be taken to avoid race conditions, as described previously.)
Sharing data among processes requires the use of IPC mechanisms, This can be
more cumbersome but makes multiple processes less likely to suffer from concurrency bugs.
IPC机制:Share Memory, Processes Semaphores, Mapped Memory, PIPE, FIFOs(named pipe), Sockets
IPC调试:ipcs
ipcrm
Shared memory is the fastest form of interprocess communication because all
processes share the same piece of memory.
A process allocates a shared memory segment using shmget.
To obtain information about a shared memory segment, pass IPC_STAT as the
second argument and a pointer to a struct shmid_ds.
To remove a segment, pass IPC_RMID as the second argument, and pass NULL as the
third argument.The segment is removed when the last process that has attached it
finally detaches it.
int getpagesize(void);
int shmget(key_t key, int size, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
The calls semget and semctl allocate and deallocate semaphores, which is analogous to
shmget and shmctl for shared memory.
Semaphores continue to exist even after all processes using them have terminated.
The last process to use a semaphore set must explicitly remove it to ensure that the
operating system does not run out of semaphores.
Unlike shared memory segments, removing a semaphore set causes Linux to deallocate immediately.
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, unsigned nsops);
The fields of struct sembuf are listed here:
sem_num is the semaphore number in the semaphore set on which the operation
is performed.
sem_op is an integer that specifies the semaphore operation.
If sem_op is a positive number, that number is added to the semaphore value
immediately.
If sem_op is a negative number, the absolute value of that number is subtracted
from the semaphore value. If this would make the semaphore value negative, the
call blocks until the semaphore value becomes as large as the absolute value of
sem_op (because some other process increments it).
If sem_op is zero, the operation blocks until the semaphore value becomes zero.
sem_flg is a flag value. Specify IPC_NOWAIT to prevent the operation from
blocking; if the operation would have blocked, the call to semop fails instead.
If you specify SEM_UNDO, Linux automatically undoes the operation on the
semaphore when the process exits.
Mapped memory permits different processes to communicate via a shared file.
Mapped memory can be used for interprocess communication or as an easy way to access
the contents of a file.
To map an ordinary file to a process’s memory, use the mmap call.
When you’re finished with a memory mapping, release it by using munmap.
void * mmap(void *start, size_t length, int prot, int flags, int fd,
off_t offset);
int munmap(void *start, size_t length);
One advanced and powerful technique used by some programs is to build data
structures in a memory-mapped file. On a subsequent invocation, the program maps that
file back into memory, and the data structures are restored to their previous state.
Another handy technique is to map the special /dev/zero file into memory.
Custom memory allocators often map /dev/zero to obtain chunks of preinitialized memory.
Pipes are serial devices; the data is always read from the pipe in the same order it was written.
A pipe’s data capacity is limited.
To create a pipe, invoke the pipe command. Supply an integer array of size 2.The call
to pipe stores the reading file descriptor in array position 0 and the writing file
descriptor in position 1.
int pipe(int filedes[2]);
int dup2(int oldfd, int newfd);
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
The first argument to popen is executed as a shell command in a subprocess running
/bin/sh.If the second argument is “r”, the function returns the child
process’s standard output stream so that the parent can read the output. If the second
argument is “w”, the function returns the child process’s standard input stream so that
the parent can send data.
A first-in, first-out (FIFO) file is a pipe that has a name in the filesystem.
Create a FIFO programmatically using the mkfifo function.
Access a FIFO just like an ordinary file.
int mkfifo(const char *pathname, mode_t mode);
These are the system calls involving sockets:
socket—Creates a socket
close—Destroys a socket
connect—Creates a connection between two sockets
bind—Labels a server socket with an address
listen—Configures a socket to accept conditions
accept—Accepts a connection and creates a new socket for the connection
Sockets connecting processes on the same computer can use the local namespace
represented by the synonyms PF_LOCAL and PF_UNIX.These are called local sockets or
UNIX-domain sockets.
socket (PF_LOCAL, SOCK_STREAM, 0);
struct sockaddr_un name;
name.sun_family = AF_LOCAL;
strcpy (name.sun_path, socket_name);
bind (socket_fd, &name, SUN_LEN (&name));
listen (socket_fd, 5);
struct sockaddr_un client_name;
socklen_t client_name_len;
accept (socket_fd, &client_name, &client_name_len);
You can create a device entry in the file system using the mknod command.
Special Devices:
/dev/null /dev/zero /dev/full /dev/random /dev/urandom loopback device(/dev/loop0)
od -t x1
dd if=/dev/zero of=/tmp/disk-image count=20480 #construct a 10MB file named disk-image
mke2fs -q /tmp/disk-image #construct an ext2 file system
mount -o loop=/dev/loop0 /tmp/disk-image /tmp/virtual-fs
The entries in /dev/pts correspond to pseudo-terminals (or pseudo-TTYs, or PTYs).
Linux creates a PTY for every new terminal window you open and displays a corresponding
entry in /dev/pts.
int ioctl(int d, int request, ...);
The ioctl system call is an all-purpose interface for controlling hardware devices.
man ioctl_list
The /proc filesystem:
/proc/cpuinfo /proc/devices /proc/pci /proc/tty/driver/serial /proc/meminfo
/proc/version /proc/sys/kernel/hostname /proc/sys/kernel/domainname
/proc/filesystems /proc/ide /proc/scsi/scsi /proc/partitions /proc/sys/dev/cdrom/info
/proc/mounts /proc/locks /proc/loadavg /proc/uptime
/proc/self /proc/[pid]/cmdline /proc/[pid]/environ /proc/[pid]/exe /proc/[pid]/fd
/proc/[pid]/statm /proc/[pid]/stat /proc/[pid]/status
The /proc/[pid]/statm entry contains a list of seven numbers, separated by spaces. Each number is
a count of the number of pages of memory used by the process in a particular category.
The categories, in the order the numbers appear, are listed here:
The total process size
The size of the process resident in physical memory
The memory shared with other processes—that is, memory mapped both by
this process and at least one other (such as shared libraries or untouched copyon-
write pages)
The text size of the process—that is, the size of loaded executable code
The size of shared libraries mapped into this process
The memory used by this process for its stack
The number of dirty pages—that is, pages of memory that have been modified
by the program
uname
setserial
uptime
sysinfo
A library function is an ordinary function that resides in a library external to your
program.
A call to a library function is just like any other function call.The arguments are
placed in processor registers or onto the stack, and execution is transferred to
the start of the function’s code, which typically resides in a loaded shared library.
A system call is implemented in the Linux kernel.When a program makes a
system call, the arguments are packaged up and handed to the kernel, which
takes over execution of the program until the call completes.
strace [exe]
The strace command traces the
execution of another program, listing any system calls the program makes and any signals
it receives.
int access(const char *pathname, int mode);
The access system call determines whether the calling process has access permission
to a file. It can check any combination of read, write, and execute permission, and it
can also check for a file’s existence.
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
If you prefer fcntl not to block if the call cannot get the lock you requested,
use F_SETLK instead of F_SETLKW. If the lock cannot be acquired, fcntl returns –1
immediately.
int fsync(int fd);
int fdatasync(int fd);
fsync fdatasync open(.., .. | O_SYNC)
int getrlimit(int resource, struct rlimit *rlim);
int getrusage(int who, struct rusage *usage);
int setrlimit(int resource, const struct rlimit *rlim);
Some of the most useful resource limits that may be changed are listed here, with
their codes:
RLIMIT_CPU—The maximum CPU time, in seconds, used by a program.This is
the amount of time that the program is actually executing on the CPU, which is
not necessarily the same as wall-clock time. If the program exceeds this time
limit, it is terminated with a SIGXCPU signal.
RLIMIT_DATA—The maximum amount of memory that a program can allocate
for its data. Additional allocation beyond this limit will fail.
RLIMIT_NPROC—The maximum number of child processes that can be running
for this user. If the process calls fork and too many processes belonging to this
user are running on the system, fork fails.
RLIMIT_NOFILE—The maximum number of file descriptors that the process may
have open at one time.
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv , const struct timezone *tz);
struct tm *localtime(const time_t *timep);
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
struct timeval tv;
struct tm* ptm;
char time_string[40];
long milliseconds;
gettimeofday (&tv, NULL);
ptm = localtime (&tv.tv_sec);
strftime (time_string, sizeof (time_string), “%Y-%m-%d %H:%M:%S”, ptm);
milliseconds = tv.tv_usec / 1000;
printf (“%s.%03ld/n”, time_string, milliseconds);
A convenient way to monitor the memory usage of your program is to use the top
command. In the output from top, the SIZE column displays the virtual address space
size of each program (the total size of your program’s code, data, and stack, some of
which may be paged out to swap space).The RSS column (for resident set size) shows
the size of physical memory that each program currently resides in.The sum of all the
RSS values for all running programs cannot exceed your computer’s physical memory
size, and the sum of all address space sizes is limited to 2GB (for 32-bit versions of
Linux).
int nanosleep(const struct timespec *req, struct timespec *rem);
The nanosleep system call is a high-precision version of the standard UNIX sleep
call. Instead of sleeping an integral number of seconds, nanosleep takes as its argument
a pointer to a struct timespec object, which can express time to nanosecond precision.
int readlink(const char *path, char *buf, size_t bufsiz);
The readlink system call retrieves the target of a symbolic link.
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
The sendfile system call provides an efficient mechanism for copying data from one
file descriptor to another.The file descriptors may be open to disk files, sockets, or
other devices.
The sendfile call can be used in many places to make copies more efficient. One
good example is in a Web server or other network daemon, that serves the contents of
a file over the network to a client program.
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimer-
val *ovalue);
unsigned int alarm(unsigned int seconds);
A program can set three different types of timers with setitimer:
If the timer code is ITIMER_REAL, the process is sent a SIGALRM signal after the
specified wall-clock time has elapsed.
If the timer code is ITIMER_VIRTUAL, the process is sent a SIGVTALRM signal after
the process has executed for the specified time.Time in which the process is not
executing is not counted.
If the timer code is ITIMER_PROF, the process is sent a SIGPROF signal when the
specified time has elapsed either during the process’s own execution or the
execution of a system call on behalf of the process.
int sysinfo(struct sysinfo *info);
int uname(struct utsname *buf);
time - time a simple command or give resource usage
id - print real and effective UIDs and GIDs
uid_t getuid(void);
uid_t geteuid(void);
int setuid(uid_t uid);
int seteuid(uid_t euid);
The trick is that the su program is a setuid program.That means that when it is
run, the effective user ID of the process will be that of the file’s owner rather than the
effective user ID of the process that performed the exec call.
int pam_start(const char *service, const char *user, const struct
pam_conv *conv, pam_handle_t **pamh_p);
int pam_authenticate(pam_handle_t *pamh, int flags);
int pam_end(pam_handle_t *pamh, int pam_status);
This facility, called Pluggable Authentication Modules, or PAM, makes it easy to write
applications that authenticate their users as the system administrator sees fit.
More Security Holes
Buffer Overruns: never use gets.
Race Conditions in /tmp
Using system or popen
You’d probably want to write a man page for the server program, for instance.This
is the first place many users will look for information about a program. Man pages are
formatted using a classic UNIX formatting system troff.
man troff
man man
If you invoke GCC with -Wall and -pedantic, the compiler
issues warnings about risky or possibly erroneous programming constructions.
Capabilities of Dynamic Memory-Checking Tools
Erroneous Behavior malloc Checking mtrace ccmalloc Electric Fence
===================================================================================
Read before allocating memory
Write before allocating memory
Read before beginning of allocation X
Write before beginning of allocation O O X
Read after end of allocation X
Write after end of allocation X X
Read after deallocation X
Write after deallocation X
Failure to deallocate memory X X
Deallocating memory twice X X
Deallocating nonallocated memory X X
Zero-size memory allocation X X
Using the profiler gprof, you can determine which functions require the most execution
time
原书信息:
名称:Advanced Linux Programming
版本:FIRST EDITION: June, 2001
作者:Mark Mitchell, Jeffrey Oldham, and Alex Samuel
读书笔记:
g++ -c -D NDEBUG=3 reciprocal.cpp
gcc -c -DNDEBUG test.c
gdb调试
gdb [exe]
run [arglist]
continue
where
up [level]
list [file]:[start,stop]
break [where]
step #step in
next #step over
man page
(1)user commands
(2)system calls
(3)standard library functions
(8)system/administrative commands
whatis [name]
man -k [keyword]
info
Among the most useful Info documents are these:
gcc—The gcc compiler
libc—The GNU C library, including many system calls
gdb—The GNU debugger
emacs—The Emacs text editor
info—The Info system itself
the system header files dir:
/usr/include
/usr/include/sys
a lot of the nitty-gritty details of how the system calls work are reflected in header files dir:
/usr/include/bits
/usr/include/asm
/usr/include/linux
GNU coding standards
info "(standards)User Interfaces"
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
You invoke the getopt_long function, passing it the argc and argv arguments to main,
the character string describing short options, and the array of struct option elements
describing the long options.
Each time you call getopt_long, it parses a single option, returning the shortoption
letter for that option, or –1 if no more options are found.
Typically, you’ll call getopt_long in a loop, to process all the options the user has
specified, and you’ll handle the specific options in a switch statement.
If getopt_long encounters an invalid option (an option that you didn’t specify as
a valid short or long option), it prints an error message and returns the character
? (a question mark). Most programs will exit in response to this, possibly after
displaying usage information.
When handling an option that takes an argument, the global variable optarg
points to the text of that argument.
After getopt_long has finished parsing all the options, the global variable optind
contains the index (into argv) of the first nonoption argument.
stdout是带缓冲的,而stderr是不带缓冲的,fflush(stdout)
extern char** environ;
char *getenv(const char *name);
int mkstemp(char *template);
Temporary files created with mkstemp are not deleted automatically. It’s up to you
to remove the temporary file when it’s no longer needed.
If the temporary file is for internal use
only and won’t be handed to another program, it’s a good idea to call unlink on the
temporary file immediately.
FILE *tmpfile (void);
The temporary file is already unlinked, as in
the previous example, so it is deleted automatically when the file pointer is closed
(with fclose) or when the program terminates.
GNU/Linux provides several other functions for generating temporary files and temporary
filenames, including mktemp, tmpnam, and tempnam. Don’t use these functions,
though, because they suffer from the reliability and security problems already mentioned.
With NDEBUG set, appearances of the assert macro will be preprocessed away. It’s a
good idea to do this only when necessary for performance reasons, though, and only
with performance-critical source files.
Use assert for internal runtime checks only
Some good places to use assert are these:
Check against null pointers, for instance, as invalid function arguments
Check conditions on function parameter values.
#include <errno.h>
#include <string.h>
extern int errno;
char *strerror(int errnum);
void perror(const char *s);
If you decide to return from the middle of a function, it’s important to make sure
that any resources successfully allocated previously in the function are first deallocated.
These resources can include memory, file descriptors, file pointers, temporary files,
synchronization objects, and so on.
Linux cleans up allocated memory, open files, and most other resources when a program
terminates, so it’s not necessary to deallocate buffers and close files before calling
exit.
Because the linker searches the archive when it is encountered on the command
line, it usually makes sense to put archives at the end of the command line.
gcc -o app -L. -ltest app.o #error
gcc -o app app.o -L. –ltest #right
When the program is actually run, the system searches for the shared
library and loads it.The system searches only /lib and /usr/lib, by default. If a shared
library that is linked into your program is installed outside those directories, it will not
be found, and the system will refuse to run the program.
One solution to this problem is to use the -Wl,-rpath option when linking the
program. Suppose that you use this:
% gcc -o app app.o -L. -ltest -Wl,-rpath,/usr/local/lib
Another solution to this problem is to set the LD_LIBRARY_PATH environment
variable when running the program. Like the PATH environment variable,
LD_LIBRARY_PATH is a colon-separated list of directories. For example, if
LD_LIBRARY_PATH is /usr/local/lib:/opt/lib, then /usr/local/lib and /opt/lib
will be searched before the standard /lib and /usr/lib directories.You should also
note that if you have LD_LIBRARY_PATH, the linker will search the directories given
there in addition to the directories given with the -L option when it is building an
executable.
The standard C library math functions are not included in libc;
instead, they’re in a separate library, libm, which you need to specify explicitly.
Pros and Cons:
One major advantage of a shared library is that it saves space on the system where
the program is installed.
A related advantage to shared libraries is that users can upgrade the libraries without
upgrading all the programs that depend on them.
The fact that an upgrade to a shared library affects all programs that depend on it
can be a disadvantage.
If you’re not going to be able to install your libraries in /lib or /usr/lib, you
should definitely think twice about using a shared library.
void *dlopen(const char *filename, int flag);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle);
pid_t getpid(void);
pid_t getppid(void);
ps -e -o pid,ppid,cmd,stat,time,tty
int system(const char *string);
In fact, system creates a subprocess running the standard Bourne shell (/bin/sh)
and hands the command to that shell for execution.
The system function returns the exit status of the shell command. If the shell itself
cannot be run, system returns 127; if another error occurs, system returns –1.
it’s preferable to use the fork and exec method for
creating processes.
To spawn a new process, you first use fork to
make a copy of the current process.Then you use exec to transform one of these
processes into an instance of the program you want to spawn.
pid_t fork(void);
Within the exec family, there are functions that vary slightly in their capabilities
and how they are called.
Functions that contain the letter p in their names (execvp and execlp) accept a
program name and search for a program by that name in the current execution
path; functions that don’t contain the p must be given the full path of the program
to be executed.
Functions that contain the letter v in their names (execv, execvp, and execve)
accept the argument list for the new program as a NULL-terminated array of
pointers to strings. Functions that contain the letter l (execl, execlp, and
execle) accept the argument list using the C language’s varargs mechanism.
Functions that contain the letter e in their names (execve and execle) accept an
additional argument, an array of environment variables.The argument should be
a NULL-terminated array of pointers to character strings. Each character string
should be of the form “VARIABLE=value”.
Because exec replaces the calling program with another one, it never returns unless an
error occurs.
When you use an exec function in your programs, you should pass the name of the function
as the first element of the argument list.
To change the niceness of a running process programmatically, use the nice function.
Its argument is an increment value, which is added to the niceness value of the
process that calls it. Remember that a positive value raises the niceness value and thus
reduces the process’s execution priority.
Note that only a process with root privilege can run a process with a negative niceness
value or reduce the niceness value of a running process.
The SIGTERM signal asks a process to terminate; the process may
ignore the request by masking or ignoring the signal.The SIGKILL signal always kills the process
immediately because the process may not mask or ignore SIGKILL.
The SIGHUP signal is sometimes used for this purpose as well, commonly to wake up
an idling program or cause a program to reread its configuration files.
Because signals are asynchronous, the main program may be in a very fragile state
when a signal is processed and thus while a signal handler function executes.
Therefore, you should avoid performing any I/O operations or calling most library
and system functions from signal handlers.
int sigaction(int signum, const struct sigaction *act, struct sigaction
*oldact);
Linux guarantees that assignments to variables of this type(sig_atomic_t) are performed
in a single instruction and therefore cannot be interrupted midway. In Linux,
sig_atomic_t is an ordinary int; in fact, assignments to integer types the size of int or
smaller, or to pointers, are atomic. If you want to write a program that’s portable to
any standard UNIX system, though, use sig_atomic_t for these global variables.
SIGBUS, SIGSEGV, and SIGFPE cause the process to terminate.
To send a signal from a program, use the kill function.
int kill(pid_t pid, int sig);
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
the WEXITSTATUS macro extracts the child process’s exit code.
You can use the WIFEXITED macro to determine from a child process’s exit status
whether that process exited normally (via the exit function or returning from main)
or died from an unhandled signal. In the latter case, use the WTERMSIG macro to extract
from its exit status the signal number by which it died.
A zombie process is a process that has terminated but has not been cleaned up yet.
When a program exits, its children are inherited by a special process, The
init process automatically cleans up any zombie child processes that it inherits.
When a child process terminates, Linux sends the parent process the SIGCHLD signal.
Thus, an easy way to clean up child processes is by handling SIGCHLD.
Make sure that any data you pass to a thread by reference is
not deallocated, even by a different thread, until you’re sure that the thread is done with
it.This is true both for local variables, which are deallocated when they go out of
scope, and for heap-allocated variables, which you deallocate by calling free (or using
delete in C++).
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void *
(*start_routine)(void *), void * arg);
int pthread_join(pthread_t th, void **thread_return);
if (!pthread_equal (pthread_self (), other_thread))
pthread_join (other_thread, NULL);
To specify customized thread attributes, you must follow these steps:
1. Create a pthread_attr_t object.The easiest way is simply to declare an automatic
variable of this type.
2. Call pthread_attr_init, passing a pointer to this object.This initializes the
attributes to their default values.
3. Modify the attribute object to contain the desired attribute values.
4. Pass a pointer to the attribute object when calling pthread_create.
5. Call pthread_attr_destroy to release the attribute object.The pthread_attr_t
variable itself is not deallocated; it may be reinitialized with pthread_attr_init.
For most GNU/Linux application programming tasks, only one thread attribute is
typically of interest.This attribute is the thread's detach state.
(joinable thread (the default) or detached thread)
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
#pass PTHREAD_CREATE_DETACHED as the second argument
int pthread_attr_destroy(pthread_attr_t *attr);
To cancel a thread, call pthread_cancel, passing the thread ID of the thread to be
canceled. A canceled thread may later be joined; in fact, you should join a canceled
thread to free up its resources.
A thread may be in one of three states with regard to thread cancellation.
The thread may be asynchronously cancelable.The thread may be canceled at any
point in its execution.
The thread may be synchronously cancelable.The thread may be canceled, but not
at just any point in its execution. Instead, cancellation requests are queued, and
the thread is canceled only when it reaches specific points in its execution.
A thread may be uncancelable. Attempts to cancel the thread are quietly ignored.
int pthread_setcanceltype(int type, int *oldtype);
int pthread_setcancelstate(int state, int *oldstate);
int old_cancel_state;
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cancel_state);
/*do some work...*/
pthread_setcancelstate (old_cancel_state, NULL);
GNU/Linux supports this by providing each thread with a
thread-specific data area.The variables stored in this area are duplicated for each thread,
and each thread may modify its copy of a variable without affecting other threads.
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void
*));
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *pointer);
void * pthread_getspecific(pthread_key_t key);
线程同步机制:Mutexes, Semaphores for Threads, Condition Variables
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex-
attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t mutex;
pthread_mutex_init (&mutex, NULL);
等同于:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Three kinds of mutexes exist:
Locking a fast mutex (the default kind) will cause a deadlock to occur.An
attempt to lock the mutex blocks until the mutex is unlocked. But because the
thread that locked the mutex is blocked on the same mutex, the lock cannot
ever be released.
Locking a recursive mutex does not cause a deadlock. A recursive mutex may
safely be locked many times by the same thread.The mutex remembers how
many times pthread_mutex_lock was called on it by the thread that holds the
lock; that thread must make the same number of calls to pthread_mutex_unlock
before the mutex is actually unlocked and another thread is allowed to lock it.
GNU/Linux will detect and flag a double lock on an error-checking mutex that
would otherwise cause a deadlock.The second consecutive call to
pthread_mutex_lock returns the failure code EDEADLK.
Each semaphore has a counter value, which is a non-negative integer.A semaphore
supports two basic operations:
A wait operation decrements the value of the semaphore by 1. If the value is
already zero, the operation blocks until the value of the semaphore becomes
positive (due to the action of some other thread).When the semaphore’s value
becomes positive, it is decremented by 1 and the wait operation returns.
A post operation increments the value of the semaphore by 1. If the semaphore
was previously zero and other threads are blocked in a wait operation on that
semaphore, one of those threads is unblocked and its wait operation completes
(which brings the semaphore’s value back to zero).
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t * sem);
int sem_trywait(sem_t * sem);
int sem_post(sem_t * sem);
int sem_getvalue(sem_t * sem, int * sval);
int sem_destroy(sem_t * sem);
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t
*cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_destroy(pthread_cond_t *cond);
Whenever your program performs an action that may change the sense of the condition
you’re protecting with the condition variable, it should perform these steps.
1. Lock the mutex accompanying the condition variable.
2. Take the action that may change the sense of the condition (in our example, set
the flag).
3. Signal or broadcast the condition variable, depending on the desired behavior.
4. Unlock the mutex accompanying the condition variable.
on GNU/Linux, threads are implemented as processes.Whenever you call pthread_create to create a
new thread, Linux creates a new process that runs that thread. However, this process is
not the same as a process you would create with fork; in particular, it shares the same
address space and resources as the original process rather than receiving copies.
The manager thread is created the first time a program calls pthread_create to create a new thread.
Here are some guidelines to help you decide which concurrency model best suits your program:
All threads in a program must run the same executable.A child process, on the
other hand, may run a different executable by calling an exec function.
An errant thread can harm other threads in the same process because threads
share the same virtual memory space and other resources. For instance, a wild
memory write through an uninitialized pointer in one thread can corrupt
memory visible to another thread.
An errant process, on the other hand, cannot do so because each process has a
copy of the program’s memory space.
Copying memory for a new process adds an additional performance overhead
relative to creating a new thread. However, the copy is performed only when
the memory is changed, so the penalty is minimal if the child process only reads
memory.
Threads should be used for programs that need fine-grained parallelism. For
example, if a problem can be broken into multiple, nearly identical tasks, threads
may be a good choice. Processes should be used for programs that need coarser
parallelism.
Sharing data among threads is trivial because threads share the same memory.
(However, great care must be taken to avoid race conditions, as described previously.)
Sharing data among processes requires the use of IPC mechanisms, This can be
more cumbersome but makes multiple processes less likely to suffer from concurrency bugs.
IPC机制:Share Memory, Processes Semaphores, Mapped Memory, PIPE, FIFOs(named pipe), Sockets
IPC调试:ipcs
ipcrm
Shared memory is the fastest form of interprocess communication because all
processes share the same piece of memory.
A process allocates a shared memory segment using shmget.
To obtain information about a shared memory segment, pass IPC_STAT as the
second argument and a pointer to a struct shmid_ds.
To remove a segment, pass IPC_RMID as the second argument, and pass NULL as the
third argument.The segment is removed when the last process that has attached it
finally detaches it.
int getpagesize(void);
int shmget(key_t key, int size, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
The calls semget and semctl allocate and deallocate semaphores, which is analogous to
shmget and shmctl for shared memory.
Semaphores continue to exist even after all processes using them have terminated.
The last process to use a semaphore set must explicitly remove it to ensure that the
operating system does not run out of semaphores.
Unlike shared memory segments, removing a semaphore set causes Linux to deallocate immediately.
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, unsigned nsops);
The fields of struct sembuf are listed here:
sem_num is the semaphore number in the semaphore set on which the operation
is performed.
sem_op is an integer that specifies the semaphore operation.
If sem_op is a positive number, that number is added to the semaphore value
immediately.
If sem_op is a negative number, the absolute value of that number is subtracted
from the semaphore value. If this would make the semaphore value negative, the
call blocks until the semaphore value becomes as large as the absolute value of
sem_op (because some other process increments it).
If sem_op is zero, the operation blocks until the semaphore value becomes zero.
sem_flg is a flag value. Specify IPC_NOWAIT to prevent the operation from
blocking; if the operation would have blocked, the call to semop fails instead.
If you specify SEM_UNDO, Linux automatically undoes the operation on the
semaphore when the process exits.
Mapped memory permits different processes to communicate via a shared file.
Mapped memory can be used for interprocess communication or as an easy way to access
the contents of a file.
To map an ordinary file to a process’s memory, use the mmap call.
When you’re finished with a memory mapping, release it by using munmap.
void * mmap(void *start, size_t length, int prot, int flags, int fd,
off_t offset);
int munmap(void *start, size_t length);
One advanced and powerful technique used by some programs is to build data
structures in a memory-mapped file. On a subsequent invocation, the program maps that
file back into memory, and the data structures are restored to their previous state.
Another handy technique is to map the special /dev/zero file into memory.
Custom memory allocators often map /dev/zero to obtain chunks of preinitialized memory.
Pipes are serial devices; the data is always read from the pipe in the same order it was written.
A pipe’s data capacity is limited.
To create a pipe, invoke the pipe command. Supply an integer array of size 2.The call
to pipe stores the reading file descriptor in array position 0 and the writing file
descriptor in position 1.
int pipe(int filedes[2]);
int dup2(int oldfd, int newfd);
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
The first argument to popen is executed as a shell command in a subprocess running
/bin/sh.If the second argument is “r”, the function returns the child
process’s standard output stream so that the parent can read the output. If the second
argument is “w”, the function returns the child process’s standard input stream so that
the parent can send data.
A first-in, first-out (FIFO) file is a pipe that has a name in the filesystem.
Create a FIFO programmatically using the mkfifo function.
Access a FIFO just like an ordinary file.
int mkfifo(const char *pathname, mode_t mode);
These are the system calls involving sockets:
socket—Creates a socket
close—Destroys a socket
connect—Creates a connection between two sockets
bind—Labels a server socket with an address
listen—Configures a socket to accept conditions
accept—Accepts a connection and creates a new socket for the connection
Sockets connecting processes on the same computer can use the local namespace
represented by the synonyms PF_LOCAL and PF_UNIX.These are called local sockets or
UNIX-domain sockets.
socket (PF_LOCAL, SOCK_STREAM, 0);
struct sockaddr_un name;
name.sun_family = AF_LOCAL;
strcpy (name.sun_path, socket_name);
bind (socket_fd, &name, SUN_LEN (&name));
listen (socket_fd, 5);
struct sockaddr_un client_name;
socklen_t client_name_len;
accept (socket_fd, &client_name, &client_name_len);
You can create a device entry in the file system using the mknod command.
Special Devices:
/dev/null /dev/zero /dev/full /dev/random /dev/urandom loopback device(/dev/loop0)
od -t x1
dd if=/dev/zero of=/tmp/disk-image count=20480 #construct a 10MB file named disk-image
mke2fs -q /tmp/disk-image #construct an ext2 file system
mount -o loop=/dev/loop0 /tmp/disk-image /tmp/virtual-fs
The entries in /dev/pts correspond to pseudo-terminals (or pseudo-TTYs, or PTYs).
Linux creates a PTY for every new terminal window you open and displays a corresponding
entry in /dev/pts.
int ioctl(int d, int request, ...);
The ioctl system call is an all-purpose interface for controlling hardware devices.
man ioctl_list
The /proc filesystem:
/proc/cpuinfo /proc/devices /proc/pci /proc/tty/driver/serial /proc/meminfo
/proc/version /proc/sys/kernel/hostname /proc/sys/kernel/domainname
/proc/filesystems /proc/ide /proc/scsi/scsi /proc/partitions /proc/sys/dev/cdrom/info
/proc/mounts /proc/locks /proc/loadavg /proc/uptime
/proc/self /proc/[pid]/cmdline /proc/[pid]/environ /proc/[pid]/exe /proc/[pid]/fd
/proc/[pid]/statm /proc/[pid]/stat /proc/[pid]/status
The /proc/[pid]/statm entry contains a list of seven numbers, separated by spaces. Each number is
a count of the number of pages of memory used by the process in a particular category.
The categories, in the order the numbers appear, are listed here:
The total process size
The size of the process resident in physical memory
The memory shared with other processes—that is, memory mapped both by
this process and at least one other (such as shared libraries or untouched copyon-
write pages)
The text size of the process—that is, the size of loaded executable code
The size of shared libraries mapped into this process
The memory used by this process for its stack
The number of dirty pages—that is, pages of memory that have been modified
by the program
uname
setserial
uptime
sysinfo
A library function is an ordinary function that resides in a library external to your
program.
A call to a library function is just like any other function call.The arguments are
placed in processor registers or onto the stack, and execution is transferred to
the start of the function’s code, which typically resides in a loaded shared library.
A system call is implemented in the Linux kernel.When a program makes a
system call, the arguments are packaged up and handed to the kernel, which
takes over execution of the program until the call completes.
strace [exe]
The strace command traces the
execution of another program, listing any system calls the program makes and any signals
it receives.
int access(const char *pathname, int mode);
The access system call determines whether the calling process has access permission
to a file. It can check any combination of read, write, and execute permission, and it
can also check for a file’s existence.
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
If you prefer fcntl not to block if the call cannot get the lock you requested,
use F_SETLK instead of F_SETLKW. If the lock cannot be acquired, fcntl returns –1
immediately.
int fsync(int fd);
int fdatasync(int fd);
fsync fdatasync open(.., .. | O_SYNC)
int getrlimit(int resource, struct rlimit *rlim);
int getrusage(int who, struct rusage *usage);
int setrlimit(int resource, const struct rlimit *rlim);
Some of the most useful resource limits that may be changed are listed here, with
their codes:
RLIMIT_CPU—The maximum CPU time, in seconds, used by a program.This is
the amount of time that the program is actually executing on the CPU, which is
not necessarily the same as wall-clock time. If the program exceeds this time
limit, it is terminated with a SIGXCPU signal.
RLIMIT_DATA—The maximum amount of memory that a program can allocate
for its data. Additional allocation beyond this limit will fail.
RLIMIT_NPROC—The maximum number of child processes that can be running
for this user. If the process calls fork and too many processes belonging to this
user are running on the system, fork fails.
RLIMIT_NOFILE—The maximum number of file descriptors that the process may
have open at one time.
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv , const struct timezone *tz);
struct tm *localtime(const time_t *timep);
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
struct timeval tv;
struct tm* ptm;
char time_string[40];
long milliseconds;
gettimeofday (&tv, NULL);
ptm = localtime (&tv.tv_sec);
strftime (time_string, sizeof (time_string), “%Y-%m-%d %H:%M:%S”, ptm);
milliseconds = tv.tv_usec / 1000;
printf (“%s.%03ld/n”, time_string, milliseconds);
A convenient way to monitor the memory usage of your program is to use the top
command. In the output from top, the SIZE column displays the virtual address space
size of each program (the total size of your program’s code, data, and stack, some of
which may be paged out to swap space).The RSS column (for resident set size) shows
the size of physical memory that each program currently resides in.The sum of all the
RSS values for all running programs cannot exceed your computer’s physical memory
size, and the sum of all address space sizes is limited to 2GB (for 32-bit versions of
Linux).
int nanosleep(const struct timespec *req, struct timespec *rem);
The nanosleep system call is a high-precision version of the standard UNIX sleep
call. Instead of sleeping an integral number of seconds, nanosleep takes as its argument
a pointer to a struct timespec object, which can express time to nanosecond precision.
int readlink(const char *path, char *buf, size_t bufsiz);
The readlink system call retrieves the target of a symbolic link.
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
The sendfile system call provides an efficient mechanism for copying data from one
file descriptor to another.The file descriptors may be open to disk files, sockets, or
other devices.
The sendfile call can be used in many places to make copies more efficient. One
good example is in a Web server or other network daemon, that serves the contents of
a file over the network to a client program.
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimer-
val *ovalue);
unsigned int alarm(unsigned int seconds);
A program can set three different types of timers with setitimer:
If the timer code is ITIMER_REAL, the process is sent a SIGALRM signal after the
specified wall-clock time has elapsed.
If the timer code is ITIMER_VIRTUAL, the process is sent a SIGVTALRM signal after
the process has executed for the specified time.Time in which the process is not
executing is not counted.
If the timer code is ITIMER_PROF, the process is sent a SIGPROF signal when the
specified time has elapsed either during the process’s own execution or the
execution of a system call on behalf of the process.
int sysinfo(struct sysinfo *info);
int uname(struct utsname *buf);
time - time a simple command or give resource usage
id - print real and effective UIDs and GIDs
uid_t getuid(void);
uid_t geteuid(void);
int setuid(uid_t uid);
int seteuid(uid_t euid);
The trick is that the su program is a setuid program.That means that when it is
run, the effective user ID of the process will be that of the file’s owner rather than the
effective user ID of the process that performed the exec call.
int pam_start(const char *service, const char *user, const struct
pam_conv *conv, pam_handle_t **pamh_p);
int pam_authenticate(pam_handle_t *pamh, int flags);
int pam_end(pam_handle_t *pamh, int pam_status);
This facility, called Pluggable Authentication Modules, or PAM, makes it easy to write
applications that authenticate their users as the system administrator sees fit.
More Security Holes
Buffer Overruns: never use gets.
Race Conditions in /tmp
Using system or popen
You’d probably want to write a man page for the server program, for instance.This
is the first place many users will look for information about a program. Man pages are
formatted using a classic UNIX formatting system troff.
man troff
man man
If you invoke GCC with -Wall and -pedantic, the compiler
issues warnings about risky or possibly erroneous programming constructions.
Capabilities of Dynamic Memory-Checking Tools
Erroneous Behavior malloc Checking mtrace ccmalloc Electric Fence
===================================================================================
Read before allocating memory
Write before allocating memory
Read before beginning of allocation X
Write before beginning of allocation O O X
Read after end of allocation X
Write after end of allocation X X
Read after deallocation X
Write after deallocation X
Failure to deallocate memory X X
Deallocating memory twice X X
Deallocating nonallocated memory X X
Zero-size memory allocation X X
Using the profiler gprof, you can determine which functions require the most execution
time