Showing posts with label Unix Internal. Show all posts
Showing posts with label Unix Internal. Show all posts

I/O Redirection and Pipes

UNIX Internals





Three Standard File Descriptors (for a process)

0: stdin, standard input - the stream of data to process
1: stdout, standard output - the stream of result data of a process
2: stderr - a stream of error messages

All Unix tools use the file descriptor 0,1,2. All 3 FDs are expected already open for reading, writing and writing, respectively.

What is a file descriptor?
Each process has a collection of files it has open. These open files are stored in an array. A file descriptor is simply an index of an item in that array.

Unix uses "the lowest-available-file-descriptor" principle - when opening a file, you always get the lowest available spot in that array.

3 ways to attach stdin to a file:
  1. close-then-open; e.g. close (0); fd=open("input_file",O_RDONLY);
  2. open-close-dup-close; e.g. open(file), close(0), dup(fd), close(fd);
  3. open-dup2-close

Summary of Redirection to Files:

  • Standard input, output, and error are file descriptors 0, 1 and 2.
  • The kernel always uses the lowest numbered unused file descriptor.
  • The set of file descriptors is passed unchanged across exec calls.
Technical Details of Pipes:
  • Pipes are NOT files.
  • A pipe is a data queue in the kernel with each end attached to a file descriptor. It is created by pipe system call.
  • Both ends of pipe are copied to a child process when the parent calls fork.
  • Pipes can only connect processes that share a common parent.
  • System calls that create file descriptors always use the lowest-numbered free file descriptor
Reading from Pipes
  • When a process tries to read from a pipe, the call blocks until some bytes are written into the pipe. What prevents a process from reading forever?
  • Reading EOF on pipe
  • Multiple readers can cause problem. A pipe is a queue. When a process reads bytes from a pipe, those bytes are no longer in the pipe. In two processes try to read from the same pipe, each process can only get partial of the data.
Writing to Pipes
  • write to a pipe blocks until there is space
  • write fails if no readers



Related System Calls:

int dup(int oldfd);
int dup2(int oldfd, int newfd);

DESCRIPTION
dup and dup2 create a copy of the file descriptor oldfd.

After successful return of dup or dup2, the old and new descriptors
may be used interchangeably. They share locks, file position pointers
and flags; for example, if the file position is modified by using
lseek on one of the descriptors, the position is also changed for the
other.

The two descriptors do not share the close-on-exec flag, however.

dup uses the lowest-numbered unused descriptor for the new descriptor.


dup2 makes newfd be the copy of oldfd, closing newfd first if neces-
sary.

RETURN VALUE
dup and dup2 return the new descriptor, or -1 if an error occurred (in
which case, errno is set appropriately).



int pipe(int filedes[2]);

DESCRIPTION
pipe creates a pair of file descriptors, pointing to a pipe inode, and
places them in the array pointed to by filedes. filedes[0] is for
reading, filedes[1] is for writing.

RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is
set appropriately.


Unix Process Control

Unix Internals:




A new process is created because an existing process makes an exact copy of itself. This child process has the same environment as its parent, only the process ID number is different. This procedure is called forking.

After the forking process, the address space of the child process is overwritten with the new process data. This is done through an exec call to the system.

The fork-and-exec mechanism thus switches an old command with a new, while the environment in which the new program is executed remains the same, including configuration of input and output devices, environment variables and priority. This mechanism is used to create all UNIX processes, so it also applies to the Linux operating system. Even the first process, init, with process ID 1, is forked during the boot procedure in the so-called bootstrapping procedure.

This scheme illustrates the fork-and-exec mechanism. The process ID changes after the fork procedure:




  • parent : read command ---->fork()---> wait(childreturnvalue) ---> receive child status

    child: -------> execvp () ---- exit (return)






[root@ipc4 uulp]# pstree -aG

init

├─acpid

├─atd

├─crond

├─cups-config-dae

├─cupsd

├─dbus-daemon-1 --system

├─(events/0)

├─(aio/0)

├─(aio/1)

├─(kacpid)

├─(kblockd/0)

├─(kblockd/1)

├─(khelper)

├─(pdflush)

└─(pdflush)

├─(events/1)

├─(kmirrord/0)

└─(kmirrord/1)

├─gdm-binary -nodaemon

├─gpm -m /dev/input/mice -t imps2

├─hald

├─irqbalance

├─(khubd)

├─(kjournald)

├─(kjournald)

├─klogd -x

├─(kseriod)

├─(ksoftirqd/0)

├─(ksoftirqd/1)

├─(kswapd0)

├─(lockd)

├─(migration/0)

├─(migration/1)

├─mingetty tty1

├─mingetty tty2

├─mingetty tty3

├─mingetty tty4

├─mingetty tty5

├─mingetty tty6

├─mysqld_safe /usr/bin/mysqld_safe --datadir=/var/lib/mysql...

└─mysqld --basedir=/ --datadir=/var/lib/mysql ...

├─named -u named -t /var/named/chroot

├─(nfsd)

├─(nfsd)

├─(nfsd)

├─(nfsd)

├─(nfsd)

├─(nfsd)

├─(nfsd)

├─(nfsd)

├─ntpd -u ntp:ntp -p /var/run/ntpd.pid -g

├─portmap

├─rpc.idmapd

├─rpc.mountd

├─rpc.rquotad

├─rpc.statd

├─rpc.yppasswdd

├─(rpciod)

├─(scsi_eh_0)

├─sendmail

├─sendmail

├─sshd

└─sshd

└─sshd

└─bash

└─sesh /bin/su -

└─su -

└─bash

├─fork2

├─fork2

├─fork2

└─+

└─fork2

├─fork2

└─fork2

└─fork2

└─pstree -aG

├─syslogd -m 0

├─udevd

├─vmware-guestd --background /var/run/vmware-guestd.pid

├─xfs -droppriv -daemon

├─xinetd -stayalive -pidfile /var/run/xinetd.pid

├─ypbind

└─ypserv

Understanding Unix Devices and Signals

Unix Internals:


Devices

In a Unix system, every device is treated as a file. Every device has a filename, an inode number, an owner, a set of permission bets, and a last-modified time. By tradition, files that represent devices are in the /dev directory, but you can crate devices files in any directory.

[root@ipc4 ~]# ls -il /dev/ | head -20
total 0
3729 crw-rw---- 1 root root 14, 12 Oct 30 02:42 adsp
439 crw------- 1 root root 10, 175 Oct 30 02:41 agpgart
443 crw------- 1 root root 36, 8 Oct 30 02:41 arpd
3907 crw------- 1 root root 14, 4 Oct 30 02:42 audio
2682 lrwxrwxrwx 1 root root 3 Oct 30 02:42 cdrom -> hdc
2684 lrwxrwxrwx 1 root root 3 Oct 30 02:42 cdwriter -> hdc
398 crw------- 1 root root 5, 1 Oct 30 10:45 console
1532 lrwxrwxrwx 1 root root 11 Oct 30 02:42 core -> /proc/kcore
839 crw------- 1 root root 10, 63 Oct 30 02:41 device-mapper
893 brw-r----- 1 root root 253, 0 Oct 30 02:41 dm-0
911 brw-r----- 1 root root 253, 1 Oct 30 02:41 dm-1
3728 crw-rw---- 1 root root 14, 9 Oct 30 02:42 dmmidi
445 crw------- 1 root root 36, 14 Oct 30 02:41 dnrtmsg
3877 crw------- 1 root root 14, 3 Oct 30 02:42 dsp
2683 lrwxrwxrwx 1 root root 3 Oct 30 02:42 dvd -> hdc
2685 lrwxrwxrwx 1 root root 3 Oct 30 02:42 dvdwriter -> hdc
431 crw------- 1 root root 13, 64 Oct 30 02:41 event0
433 crw------- 1 root root 13, 65 Oct 30 02:41 event1
1528 lrwxrwxrwx 1 root root 13 Oct 30 02:42 fd -> /proc/self/fd

Properties of Device Files

Device files have most of the properties disk files have. Below 'ls' and 'stat' commands shows the properties of a terminal /dev/pts/3 (see your current terminal by 'tty' command), it has inode number 5, permission is crw--w----, owned by root, group is 'tty', 'filesize' last modification time is Nov 1, 13:05. Note the filesize field is 136,3 instead of a regular file's size. This is because the inode of a device file stores, not a a file size and storage list (points pointing the data blocks), it stores a pointer to a subroutine in the kernel, which is called device driver. 136,3 means the device driver is subroutine number 136 in a table of device-processing code. 3 is the argument passed to the device-process code. 136 is also called a device's major number and 3 is called minor number.

Note the permission bit for pts/3, only owner can read from the file (terminal) and all others can write into it. In the example below, we can copy message of the day file to a terminal.


Device file's filetype is also stored in inode's st_mode field.





Questions - What's the command to dump your terminal to another one (assume you have permission to write to)? e.g. when you type "ls -l", it shows on your terminal and also on user's terminal simultaneously?

What you do to just show the command's output not the command itself?

How to turn on auditing mode to peek other's terminal without letting the users know?

Answers are at the end of this blog article.


[root@ipc4 ~]# ls -il /dev/pts/3
5 crw--w---- 1 root tty 136, 3 Nov 1 13:05 /dev/pts/3

[root@ipc4 ~]# stat /dev/pts/3
File: `/dev/pts/3'
Size: 0 Blocks: 0 IO Block: 1024 character special file
Device: 9h/9d Inode: 5 Links: 1 Device type: 88,3
Access: (0620/crw--w----) Uid: ( 0/ root) Gid: ( 5/ tty)
Access: 2009-11-01 13:00:58.022207816 -0800
Modify: 2009-11-01 13:00:58.022207816 -0800
Change: 2009-11-01 12:43:13.992842432 -0800



[root@ipc4 ~]# tty
/dev/pts/3

[root@ipc4 ~]# cp /etc/motd /dev/pts/3

This is a restricted Linux server running on a vmware inside Shan's laptop.



[root@ipc4 ~]# write shan /dev/pts/2

Message from root@ipc4.shanjing.com on pts/3 at 13:21 ...
Do you copy?
Do you copy?


Signals:

A signal is a one-word messages sent by kernel requested by one of the three sources:
users (control -C, sends signal code 2),
kernel ( segmentation violation),
processes (use kill system call).

Signal Value Action Comment
-------------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at tty
SIGTTIN 21,21,26 Stop tty input for background process
SIGTTOU 22,22,27 Stop tty output for background process


Job Control on Unix

control-z Stop (don't kill) the foreground job, and then return to the shell
jobs Check the status of jobs in the current session
ps -u username Check the status of processes, including those from other sessions. On BSD systems, use 'ps -gx'.
kill -9 %1 Kill a job, by specifying its job number after the percent sign
kill -9 123 Kill a process, by specifying its process id (PID) number
bg Run the most recently stopped job in the background
fg Bring most recently backgrounded job to the foreground
fg %1 Bring a job to foreground by specifying its job number after the percent sign
[shan@ipc4 uulp]$ ps -u shan
PID TTY TIME CMD
3659 ? 00:00:04 sshd
3660 pts/0 00:00:00 bash
6641 ? 00:00:00 sshd
6642 pts/2 00:00:00 bash
6773 ? 00:00:01 sshd
6774 pts/1 00:00:00 bash
6949 ? 00:00:00 sshd
6950 pts/4 00:00:00 bash
7099 pts/4 00:00:00 ps
[1]+ Exit 228 ./signal

[shan@ipc4 uulp]$ gcc -o job-1 signal.c
[shan@ipc4 uulp]$ ./job-1 &
[1] 7109
[shan@ipc4 uulp]$ jobs -l
[1]+ 7109 Running ./job-1 &

[shan@ipc4 uulp]$ fg %1
./job-1

(control Z to stop/suspend the job but don't kill)
[1]+ Stopped ./job-1
[shan@ipc4 uulp]$ jobs -l
[1]+ 7109 Stopped ./job-1

(bring it to background)
[shan@ipc4 uulp]$ bg %1
[1]+ ./job-1 &

[shan@ipc4 uulp]$ jobs -l
[1]+ 7109 Running ./job-1 &


Ignoring Control -C (SIGINT) signal:

[shan@ipc4 uulp]$ cat ignore.c
/* ignore.c - shows how to ignore a signal
* - press Ctrl-\ to kill this one
*/

#include
#include

main()
{
signal( SIGINT, SIG_IGN );

printf("you can't stop me!\n");
while( 1 )
{
sleep(1);
printf("haha\n");
}
}
[shan@ipc4 uulp]$ ./ignore
you can't stop me!
haha

haha
haha
haha
haha
^_Quit





Answers to the questions:

To dump your terminal to pts/3,
script -a /dev/null | tee /dev/pts/3

stty -echo

use script's -q option (Linux and AIX both implemented, but not on Solaris)

Unix Files and Directories

Key Concepts:

A filesystem normally has boot block, super block, i-node list, directory and data blocks.

The i-node contains all the information about the file: the file type, the file's access permission bits, the size of the file, pointers to the data blocks for the file, and so on. Most of the information in the stat structure is obtained from the i-node. Only two items are stored in the directory entry: the filename and the i-node number.

When renaming a file without changing filesystems, the actual content of the file need not be moved - all that needs to be done is to have a new directory entry point to the existing i-node and have the old directory entry removed.

Since the i-node number in the directory entry points to an i-node in the same filesystem, we cannot have a directory entry pint to an-inode in a different filesystem. This is why hardlink can't cross filesystems.

With a symbolic link, the actual contents of the file (the data blocks) contains the name of the file that the symbolic link points to.

Each i-node has a link count that contains the number of directory entries that point to the i-node. When the link count goes to 0 can the file be deleted. This is why "unlinking a file" does not always mean "deleting the blocks associated with the file." They is why the function that removes a directory is called unlink and not delete.

Any leaf directory always has a link count of 2. The value of 2 is from the directory entry that names the directory and from the entry for dot in that directory. What about the / filesystem's link count? Are the . and .. pointing to the same place or different ones?

Every directory in the working directory causes the working directory's link count to be increased by 1. (the .. entry of the new subdirectory)

Only a superuser process can create a new link that points to a directory. The reason is that doing this can cause loops in the filesystem.

When a file is closed the kernel first checks the count of the number of processes that have the file open. If this count has reached 0 then the kernel checks the link count, and if it is 0, the file's contents are deleted.

A directory is just a file containing directory entries(filenames and associate i-node numbers). Adding, deleting, or modifying these directory entries can affect all three times associated with that directory. With permissions, we can create new files in the direcotry and remove files from it, but we can't write to the directory itself. Only the kernel can write to a directory.



R
eferences:

Unix UFS i-node Structure is explained at this site.

A very good lecture note about file system is at here.

A very good brief introduction of Unix filesystems and Linux extfs and ext2fs is at : http://web.mit.edu/tytso/www/linux/ext2intro.html

For a complete description of Unix Files and Directories, please read Richard Stevens' Advanced Programming in the UNIX Environment.





stat man page.
http://www.manpages.info/linux/stat.2.html


They all return a stat structure, which contains the following fields:

struct stat {
dev_t st_dev; /* device */
ino_t st_ino; /* inode */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device type (if inode device) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last change */
};

The value st_size gives the size of the file (if it is a regular file
or a symlink) in bytes. The size of a symlink is the length of the
pathname it contains, without trailing NUL.

The value st_blocks gives the size of the file in 512-byte blocks.
(This may be smaller than st_size/512 e.g. when the file has holes.)
The value st_blksize gives the "preferred" blocksize for efficient file
system I/O. (Writing to a file in smaller chunks may cause an ineffi-
cient read-modify-rewrite.)

Not all of the Linux filesystems implement all of the time fields.
Some file system types allow mounting in such a way that file accesses
do not cause an update of the st_atime field. (See `noatime' in
mount(8).)

The field st_atime is changed by file accesses, e.g. by execve(2),
mknod(2), pipe(2), utime(2) and read(2) (of more than zero bytes).
Other routines, like mmap(2), may or may not update st_atime.

The field st_mtime is changed by file modifications, e.g. by mknod(2),
truncate(2), utime(2) and write(2) (of more than zero bytes). More-
over, st_mtime of a directory is changed by the creation or deletion of
files in that directory. The st_mtime field is not changed for changes
in owner, group, hard link count, or mode.

The field st_ctime is changed by writing or by setting inode informa-
tion (i.e., owner, group, link count, mode, etc.).



Sample code:

/* statinfo.c - demonstrates using stat() to obtain
* file information.
* - some members are just numbers...
*/
#include
#include
#include

void show_stat_info(char *, struct stat *);

int main(int ac, char *av[])
{
struct stat info; /* buffer for file info */

if (ac>1)
if( stat(av[1], &info) != -1 ){
show_stat_info( av[1], &info );
return 0;
}
else
perror(av[1]); /* report stat() errors */
return 1;
}
void show_stat_info(char *fname, struct stat *buf)
/*
* displays some info from stat in a name=value format
*/
{
printf(" inode: %o\n", buf->st_ino); /* inode */
printf(" mode: %o\n", buf->st_mode); /* type + mode */
printf(" links: %d\n", buf->st_nlink); /* # links */
printf(" user: %d\n", buf->st_uid); /* user id */
printf(" group: %d\n", buf->st_gid); /* group id */
printf(" size: %d\n", buf->st_size); /* file size */
printf("modtime: %d\n", buf->st_mtime); /* modified */
printf(" name: %s\n", fname ); /* filename */
}

Execution:

[root@ipc4 filesystems]# ./fileinfo /etc/passwd
mode: 100644
links: 1
user: 0
group: 0
size: 1888
modtime: 1256242470
name: /etc/passwd

-rw-r--r-- 1 root root 1888 Oct 22 13:14 /etc/passwd


stat -> st_mode tells us inode's type, suid,sgid,sticky, ugo permissions.
The mode (100644) or -rw-r--r-- is the value of st_mode (type+mode), the type is 1000 and
the (unix file access) mode is 644.

It's binary and its bit break down are defined as below:

1000 644 => 1000 0 0 0 110 110 100
type suid sgid sticky u(rw-) group(r--) other(r--)

For type's value, it's defined as follow in

S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 fifo

About file's a_time, c_time, m_time

The field st_atime is changed by file accesses, e.g. by execve(2),
mknod(2), pipe(2), utime(2) and read(2) (of more than zero bytes).
Other routines, like mmap(2), may or may not update st_atime.

The field st_mtime is changed by file modifications, e.g. by mknod(2),
truncate(2), utime(2) and write(2) (of more than zero bytes). More-
over, st_mtime of a directory is changed by the creation or deletion
of files in that directory. The st_mtime field is not changed for
changes in owner, group, hard link count, or mode.

The field st_ctime is changed by writing or by setting inode informa-
tion (i.e., owner, group, link count, mode, etc.).

------------------------------------




A simple 'ls' command in C:

main( argc, *argv[] )
while ( -- argc )
do do_ls ( *argv )
done

do_ls (dir[])
opendir ( dir )
dostat (direntp->d_name);
closedir(dir);

dostat ( *filename)
stat (filename, &stat_info)
show_file_info (filename, & stat_info);

show_file_info ( *filename, stat_info)
mode_to_letters (stat_info->st_mode, modestr)
uid_to_name (stat_info->st_uid)
gid_to_name (stat_info->st_gid)
utilitiy_functions
uid_to_name,
gid_to_name,
mode_to_letters;



/* lss.c
* purpose list contents of directory or directories
* action if no args, use . else list files in args
* note uses stat and pwd.h and grp.h
* BUG: try lss /tmp
*/
#include
#include
#include
#include

void do_ls(char[]);
void dostat(char *);
void show_file_info( char *, struct stat *);
void mode_to_letters( int , char [] );
char *uid_to_name( uid_t );
char *gid_to_name( gid_t );

main(int ac, char *av[])
{
if ( ac == 1 )
do_ls( "." );
else
while ( --ac ){
printf("%s:\n", *++av );
do_ls( *av );
}
}

void do_ls( char dirname[] )
/*
* list files in directory called dirname
*/
{
DIR *dir_ptr; /* the directory */
struct dirent *direntp; /* each entry */

if ( ( dir_ptr = opendir( dirname ) ) == NULL )
fprintf(stderr,"ls1: cannot open %s\n", dirname);
else
{
while ( ( direntp = readdir( dir_ptr ) ) != NULL )
dostat( direntp->d_name );
closedir(dir_ptr);
}
}

void dostat( char *filename )
{
struct stat info;

if ( stat(filename, &info) == -1 ) /* cannot stat */
perror( filename ); /* say why */
else /* else show info */
show_file_info( filename, &info );
}

void show_file_info( char *filename, struct stat *info_p )
/*
* display the info about 'filename'. The info is stored in struct at *info_p
*/
{
char *uid_to_name(), *ctime(), *gid_to_name(), *filemode();
void mode_to_letters();
char modestr[11];

mode_to_letters( info_p->st_mode, modestr );

printf( "%s" , modestr );
printf( "%4d " , (int) info_p->st_nlink);
printf( "%-8s " , uid_to_name(info_p->st_uid) );
printf( "%-8s " , gid_to_name(info_p->st_gid) );
printf( "%8ld " , (long)info_p->st_size);
printf( "%.12s ", 4+ctime(&info_p->st_mtime));
printf( "%s\n" , filename );

}

/*
* utility functions
*/

/*
* This function takes a mode value and a char array
* and puts into the char array the file type and the
* nine letters that correspond to the bits in mode.
* NOTE: It does not code setuid, setgid, and sticky
* codes
*/
void mode_to_letters( int mode, char str[] )
{
strcpy( str, "----------" ); /* default=no perms */

if ( S_ISDIR(mode) ) str[0] = 'd'; /* directory? */
if ( S_ISCHR(mode) ) str[0] = 'c'; /* char devices */
if ( S_ISBLK(mode) ) str[0] = 'b'; /* block device */

if ( mode & S_IRUSR ) str[1] = 'r'; /* 3 bits for user */
if ( mode & S_IWUSR ) str[2] = 'w';
if ( mode & S_IXUSR ) str[3] = 'x';

if ( mode & S_IRGRP ) str[4] = 'r'; /* 3 bits for group */
if ( mode & S_IWGRP ) str[5] = 'w';
if ( mode & S_IXGRP ) str[6] = 'x';

if ( mode & S_IROTH ) str[7] = 'r'; /* 3 bits for other */
if ( mode & S_IWOTH ) str[8] = 'w';
if ( mode & S_IXOTH ) str[9] = 'x';
}

#include

char *uid_to_name( uid_t uid )
/*
* returns pointer to username associated with uid, uses getpw()
*/
{
struct passwd *getpwuid(), *pw_ptr;
static char numstr[10];

if ( ( pw_ptr = getpwuid( uid ) ) == NULL ){
sprintf(numstr,"%d", uid);
return numstr;
}
else
return pw_ptr->pw_name ;
}

#include

char *gid_to_name( gid_t gid )
/*
* returns pointer to group number gid. used getgrgid(3)
*/
{
struct group *getgrgid(), *grp_ptr;
static char numstr[10];

if ( ( grp_ptr = getgrgid(gid) ) == NULL ){
sprintf(numstr,"%d", gid);
return numstr;
}
else
return grp_ptr->gr_name;
}

[root@ipc4 filesystems]# ./lss /home/shan/prog/unix .
/home/shan/prog/unix:
drwxr-xr-x 2 root root 4096 Oct 28 22:00 .
drwxr-xr-x 3 root root 4096 Oct 28 17:23 ..
filesystems: No such file or directory
.:
-rw-r--r-- 1 root root 3446 Oct 28 17:00 ls2.c
-rwxr-xr-x 1 root root 5382 Oct 28 19:09 fileinfo
-rw-r--r-- 1 root root 332 Oct 28 16:58 Makefile
-rwxr-xr-x 1 root root 7101 Oct 28 19:43 ls2
drwxr-xr-x 2 root root 4096 Oct 28 22:00 .
-rwxr-xr-x 1 root root 5075 Oct 28 17:00 filesize
drwxr-xr-x 3 root root 4096 Oct 28 17:23 ..
-rw-r--r-- 1 root root 322 Oct 28 16:58 filesize.c
-rw-r--r-- 1 root root 1216 Oct 28 19:09 fileinfo.c
-rw-r--r-- 1 root root 4467 Oct 28 19:12 fs.txt
-rw-r--r-- 1 root root 9524 Oct 28 19:04 stat.man
-rwxr-xr-x 1 root root 5382 Oct 28 19:09 f
-rw-r--r-- 1 root root 718 Oct 28 16:59 ls1.c
-rwxr-xr-x 1 root root 7101 Oct 28 22:00 lss
-rw-r--r-- 1 root root 3446 Oct 28 21:58 lss.c




Unix Processes

MultiProcesses vs MultiThreaded




Unix Program Image in Main Memory

A program is a sequence of machine-language instructions stored in a file. Running a program means loading that list of machine-language instructions into memory and having the CPU execute the instructions, one by one.

Let's spend 1 minute to find out what is a process and how it is loaded into the memory.

A process is the memory space and settings with which the program runs.


To look at the processes from Unix SA's prospective, let's start with Unix command 'ps'.
[root@ipc4 ~]# ps -fal
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 S root 16611 3560 0 76 0 - 1487 - 11:14 tty7 0:00:00 /usr/
4 S root 16635 16522 0 77 0 - 1393 wait 11:20 pts/2 0:00:00 su
4 S root 16636 16635 0 75 0 - 1463 wait 11:20 pts/2 0:00:00 -bash
0 S shan 16710 15966 0 78 0 - 5570 322557
11:38 pts/0 0:00:00 ./multi
4 S root 16714 16166 0 77 0 - 1101 wait 11:39 pts/1 0:00:00 su
4 S root 16715 16714 0 77 0 - 1438 wait 11:39 pts/1 0:00:00 -bash
4 S root 16749 16715 0 60 -20 - 5996 322557
11:39 pts/1 0:00:00 ./multi
4 R root 16755 16636 0 77 0 - 729 - 11:43 pts/2 0:00:00 ps

The column marked S shows the status of each process. R is running, S is sleeping. WCHAN shows why a process is sleeping.

A process has a size, shown in the SZ column. This number
represents the amount of memory the process is using. The size of a process change as it runs. A program that allocates memory when it runs can get larger. Memory in a Unix system is divided into kernel space and user space. Processes live in user space.

Columns marked PRI and NI contain priority and nicesness levels of the process. The kernel uses these values to decide when to run processes or the priority of the processes.

TTY shows which terminal the process is connected to. If no tty, the process is started at startup.

Among the above processes, there are two instances of program "./multi", one runs under user "shan" and another runs under "root". The one under "root" pid 16749's nice value is "-20" and priority is "60", which has much higher priority than the one runs under "shan". That's because the root started multi with nice value of -20.
You can use command "renice" to adjust the nice value of a process.

#nice -n -20 ./multi


The commands "ps ax" or "ps lax" (in linux) will show you all the running system processes, you will realize that a lot of processes don't have terminal ( TTY column marked ?) because they are started when the system starts up not typed by a user at command line.


[root@ipc4 prog]# ps ax
PID TTY STAT TIME COMMAND
1 ? S 0:00 init [5]
2 ? S 0:00 [migration/0]
3 ? SN 0:00 [ksoftirqd/0]
4 ? S 0:00 [migration/1]
5 ? SN 0:00 [ksoftirqd/1]
6 ? S 0:00 [events/0]
7 ? S 0:00 [events/1]
8 ? S 0:00 [khelper]
9 ? S 0:00 [kacpid]
22 ? S 0:00 [kblockd/0]
23 ? S 0:00 [kblockd/1]
33 ? S 0:00 [pdflush]
24 ? S 0:00 [khubd]
34 ? S 0:00 [pdflush]
36 ? S 0:00 [aio/0]
37 ? S 0:00 [aio/1]
35 ? S 0:01 [kswapd0]
111 ? S 0:00 [kseriod]
182 ? S 0:00 [scsi_eh_0]
194 ? S 0:00 [kmirrord/0]
195 ? S 0:00 [kmirrord/1]
203 ? S 0:03 [kjournald]
982 ? Ss 0:00 udevd
1668 ? S 0:00 [kjournald]
2175 ? Ss 0:00 syslogd -m 0
2179 ? Ss 0:00 klogd -x
2190 ? Ss 0:08 irqbalance
2208 ? Ss 0:00 portmap
2228 ? Ss 0:00 rpc.statd
2261 ? Ss 0:00 rpc.idmapd
2306 ? S 0:00 /usr/local/sbin/ypserv
2309 ? S 0:00 /usr/local/sbin/rpc.yppasswdd
2317 ? Sl 0:00 ypbind
2372 ? Ss 0:00 /usr/sbin/acpid
2435 ? Ss 0:52 /usr/sbin/vmware-guestd --background /var/run/vmware-
2491 ? Ssl 0:00 /usr/sbin/named -u named -t /var/named/chroot
2504 ? Ss 0:00 /usr/sbin/sshd
2532 ? SLs 0:00 ntpd -u ntp:ntp -p /var/run/ntpd.pid -g
2552 ? Ss 0:02 sendmail: accepting connections
2560 ? Ss 0:00 sendmail: Queue runner@01:00:00 for /var/spool/client
2571 ? Ss 0:00 gpm -m /dev/input/mice -t imps2
2591 ? Ss 0:00 crond
2617 ? Ss 0:00 xfs -droppriv -daemon
2636 ? Ss 0:00 /usr/sbin/atd
2655 ? Ssl 0:00 dbus-daemon-1 --system
2666 ? Ss 0:00 cups-config-daemon
2677 ? Ss 0:26 hald
2699 ? S 0:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql
2713 tty1 Ss+ 0:00 /sbin/mingetty tty1
2715 tty2 Ss+ 0:00 /sbin/mingetty tty2

Process is program in action.

How shell runs program?

A. User types program's name 'a.out'

B. The shell creates a new process to run the program ;

C. The shell loads the program from the disk into the process;
(done by execvp( const *prog, const char *argv[])

D. The program runs in its process until it is done.

Note: The kernel loads the program from the disk into CURRENT process, replacing the coe and data of current process.

In the following code, the message "ls is done. bye." will NOT be displayed, because it was erased by execvp.

The exec system call clears out the current program code from the current process, then, in the now empty process, puts the code of the program named in the exec call, and then runs that new program. exec also changes the memory allocation of the process to fit the space requirement of the new program. Note, the following code
printf("* * * ls is done. bye\n") will not be executed.

/* exec1.c
*/

main()
{
char *arglist[3];

arglist[0] = "ls";
arglist[1] = "-l";
arglist[2] = 0 ;
printf("* * * About to exec ls -l\n");
execvp( "ls" , arglist );
printf("* * * ls is done. bye\n");
}

[shan@ipc4 uulp]$ ./exec
* * * About to exec ls -l
total 124
-rw-rw-r-- 1 shan shan 2593 Nov 4 12:37 bounce.c
-rw-rw-r-- 1 shan shan 476 Nov 4 12:38 bounce.h
-rwxrwxr-x 1 shan shan 4924 Nov 4 15:03 exec
-rw-rw-r-- 1 shan shan 262 Nov 4 15:03 exec.c
-rw-rw-r-- 1 shan shan 1300 Nov 4 15:04 exec.out
drwxrwxr-x 2 shan shan 4096 Nov 4 12:43 game
-rwxrwxr-x 1 shan shan 4975 Nov 4 12:31 ignore
-rw-rw-r-- 1 shan shan 258 Nov 4 12:31 ignore.c
-rwxrwxr-x 1 shan shan 4989 Nov 4 12:21 job-1
-rwxrwxr-x 1 shan shan 6710 Nov 4 12:28 play
-rw-rw-r-- 1 shan shan 3271 Nov 4 12:28 play.c
-rwxrwxr-x 1 shan shan 5013 Nov 4 12:12 signal
-rw-rw-r-- 1 shan shan 399 Nov 4 12:21 signal.c




From Unix internal, let's see a process' memory layout:

1. Text Segment.
This is the machine instructions that are executed by CPU.

2.
Initialized data segment.
It contains variables that are specifically initialized in the program.


3. Uninitialized data segment or "bss" (block started by symbol).
Data in this segment is initialized by the kernel (0 or null pointers) before the program starts executing. e.g. C declaration long sum[2000];


4. Stack.
This is where automatic variables are stored, along with information that is saved each time a function is called.


5. Heap.
Dynamic memory allocation usually takes place in heap.


Unix 'size' command reports the sizes in bytes of the text, data, and bss segments.

[root@ipc4 prog]# size ~shan/prog/multi
text data bss dec hex filename
1389 280 8 1677 68d /home/shan/prog/multi

[root@ipc4 prog]# size /usr/bin/gcc

text data bss dec hex filename

89316 1576 932 91824 166b0 /usr/bin/gcc



Now, let's see how process (a running program) is loaded into the memory:



* In Linux processes loaded from a file system (using either the execve() or spawn() system calls) are in ELF format.

* If the file system is on a block-oriented device, the code and data are loaded into main memory.

* If the file system is memory mapped (e.g. ROM/Flash image), the code needn't be loaded into RAM, but may be executed in place.

* This approach makes all RAM available for data and stack, leaving the code in ROM or Flash. In all cases, if the same process is loaded more than once, its code will be shared.

* Before we can run an executable, firstly we have to load it into memory.

* This is done by the loader, which is generally part of the operating system. The loader does the following things (from other things):

1. Memory and access validation - Firstly, the OS system kernel reads in the program file’s header information and does the validation for type, access permissions, memory requirement and its ability to run its instructions. It confirms that file is an executable image and calculates memory requirements.

2. Process setup includes:

1) Allocates primary memory for the program's execution.
2) Copies address space from secondary to primary memory.
3) Copies the .text and .data sections from the executable into primary memory.
4) Copies program arguments (e.g., command line arguments) onto the stack.
5) Initializes registers: sets the esp (stack pointer) to point to top of stack, clears the rest.
6) Jumps to start routine, which: copies main()'s arguments off of the stack, and jumps to main().

* Address space is memory space that contains program code, stack, and data segments or in other word, all data the program uses as it runs.

* The memory layout, consists of three segments (text, data, and stack), see the attached figure.

* The dynamic data segment is also referred to as the heap, the place dynamically allocated memory (such as from malloc() and new) comes from. Dynamically allocated memory is memory allocated at run time instead of compile/link time.

* This organization enables any division of the dynamically allocated memory between the heap (explicitly) and the stack (implicitly). This explains why the stack grows downward and heap grows upward.



Below is a multithreaded C code, let's compile it and dump the object file content using readelf program.

[root@ipc4 prog]# cat multi.c
#include
#include

#define NUM 500

main()
{
pthread_t t1,t2;

long sum[1000];
int maxcount=99;

void *print_msg(void *);

pthread_create(&t1, NULL, print_msg, (void *) "hello");
pthread_create(&t2, NULL, print_msg, (void *) " world\n");
pthread_join(t1,NULL);
pthread_join(t2,NULL);
}

void *print_msg(void *m)
{
char *cp = (char *) m;
int i;
for (i=0 ; i<>
printf("%s",m);
fflush(stdout);
sleep(1);
}
return NULL;
}

[root@ipc4 prog]# gcc -g -v multi.c -lpthread -o multi
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.3 20050227 (Red Hat 3.4.3-22.1)
/usr/libexec/gcc/i386-redhat-linux/3.4.3/cc1 -quiet -v multi.c -quiet -dumpbase multi.c -auxbase multi -g -version -o /tmp/ccoHQEeG.s
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/3.4.3/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/3.4.3/include
/usr/include
End of search list.
GNU C version 3.4.3 20050227 (Red Hat 3.4.3-22.1) (i386-redhat-linux)
compiled by GNU C version 3.4.3 20050227 (Red Hat 3.4.3-22.1).
GGC heuristics: --param ggc-min-expand=64 --param ggc-min-heapsize=64314
as -V -Qy -o /tmp/ccw7Cttf.o /tmp/ccoHQEeG.s
GNU assembler version 2.15.92.0.2 (i386-redhat-linux) using BFD version 2.15.92.0.2 20040927
/usr/libexec/gcc/i386-redhat-linux/3.4.3/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o multi /usr/lib/gcc/i386-redhat-linux/3.4.3/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/3.4.3/../../../crti.o /usr/lib/gcc/i386-redhat-linux/3.4.3/crtbegin.o -L/usr/lib/gcc/i386-redhat-linux/3.4.3 -L/usr/lib/gcc/i386-redhat-linux/3.4.3 -L/usr/lib/gcc/i386-redhat-linux/3.4.3/../../.. /tmp/ccw7Cttf.o -lpthread -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i386-redhat-linux/3.4.3/crtend.o /usr/lib/gcc/i386-redhat-linux/3.4.3/../../../crtn.o





[root@ipc4 prog]# readelf -hS ~shan/prog/multi
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048410
Start of program headers: 52 (bytes into file)
Start of section headers: 2460 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 28
Section header string table index: 25

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048114 000114 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048128 000128 000020 00 A 0 0 4
[ 3] .hash HASH 08048148 000148 000040 04 A 4 0 4
[ 4] .dynsym DYNSYM 08048188 000188 0000b0 10 A 5 1 4
[ 5] .dynstr STRTAB 08048238 000238 0000aa 00 A 0 0 1
[ 6] .gnu.version VERSYM 080482e2 0002e2 000016 02 A 4 0 2
[ 7] .gnu.version_r VERNEED 080482f8 0002f8 000050 00 A 5 2 4
[ 8] .rel.dyn REL 08048348 000348 000010 08 A 4 0 4
[ 9] .rel.plt REL 08048358 000358 000030 08 A 4 11 4
[10] .init PROGBITS 08048388 000388 000017 00 AX 0 0 4
[11] .plt PROGBITS 080483a0 0003a0 000070 04 AX 0 0 4
[12] .text PROGBITS 08048410 000410 00023c 00 AX 0 0 4
[13] .fini PROGBITS 0804864c 00064c 00001a 00 AX 0 0 4
[14] .rodata PROGBITS 08048668 000668 000019 00 A 0 0 4
[15] .eh_frame PROGBITS 08048684 000684 000004 00 A 0 0 4
[16] .ctors PROGBITS 08049688 000688 000008 00 WA 0 0 4
[17] .dtors PROGBITS 08049690 000690 000008 00 WA 0 0 4
[18] .jcr PROGBITS 08049698 000698 000004 00 WA 0 0 4
[19] .dynamic DYNAMIC 0804969c 00069c 0000d0 08 WA 5 0 4
[20] .got PROGBITS 0804976c 00076c 000004 04 WA 0 0 4
[21] .got.plt PROGBITS 08049770 000770 000024 04 WA 0 0 4
[22] .data PROGBITS 08049794 000794 00000c 00 WA 0 0 4
[23] .bss NOBITS 080497a0 0007a0 000008 00 WA 0 0 4
[24] .comment PROGBITS 00000000 0007a0 000123 00 0 0 1
[25] .shstrtab STRTAB 00000000 0008c3 0000d7 00 0 0 1
[26] .symtab SYMTAB 00000000 000dfc 0004c0 10 27 44 4
[27] .strtab STRTAB 00000000 0012bc 0002a8 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
[root@ipc4 prog]# readelf -a ~shan/prog/multi
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048410
Start of program headers: 52 (bytes into file)
Start of section headers: 2460 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 28
Section header string table index: 25

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048114 000114 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048128 000128 000020 00 A 0 0 4
[ 3] .hash HASH 08048148 000148 000040 04 A 4 0 4
[ 4] .dynsym DYNSYM 08048188 000188 0000b0 10 A 5 1 4
[ 5] .dynstr STRTAB 08048238 000238 0000aa 00 A 0 0 1
[ 6] .gnu.version VERSYM 080482e2 0002e2 000016 02 A 4 0 2
[ 7] .gnu.version_r VERNEED 080482f8 0002f8 000050 00 A 5 2 4
[ 8] .rel.dyn REL 08048348 000348 000010 08 A 4 0 4
[ 9] .rel.plt REL 08048358 000358 000030 08 A 4 11 4
[10] .init PROGBITS 08048388 000388 000017 00 AX 0 0 4
[11] .plt PROGBITS 080483a0 0003a0 000070 04 AX 0 0 4
[12] .text PROGBITS 08048410 000410 00023c 00 AX 0 0 4
[13] .fini PROGBITS 0804864c 00064c 00001a 00 AX 0 0 4
[14] .rodata PROGBITS 08048668 000668 000019 00 A 0 0 4
[15] .eh_frame PROGBITS 08048684 000684 000004 00 A 0 0 4
[16] .ctors PROGBITS 08049688 000688 000008 00 WA 0 0 4
[17] .dtors PROGBITS 08049690 000690 000008 00 WA 0 0 4
[18] .jcr PROGBITS 08049698 000698 000004 00 WA 0 0 4
[19] .dynamic DYNAMIC 0804969c 00069c 0000d0 08 WA 5 0 4
[20] .got PROGBITS 0804976c 00076c 000004 04 WA 0 0 4
[21] .got.plt PROGBITS 08049770 000770 000024 04 WA 0 0 4
[22] .data PROGBITS 08049794 000794 00000c 00 WA 0 0 4
[23] .bss NOBITS 080497a0 0007a0 000008 00 WA 0 0 4
[24] .comment PROGBITS 00000000 0007a0 000123 00 0 0 1
[25] .shstrtab STRTAB 00000000 0008c3 0000d7 00 0 0 1
[26] .symtab SYMTAB 00000000 000dfc 0004c0 10 27 44 4
[27] .strtab STRTAB 00000000 0012bc 0002a8 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00688 0x00688 R E 0x1000
LOAD 0x000688 0x08049688 0x08049688 0x00118 0x00120 RW 0x1000
DYNAMIC 0x00069c 0x0804969c 0x0804969c 0x000d0 0x000d0 RW 0x4
NOTE 0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4

Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag
06

Dynamic section at offset 0x69c contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000c (INIT) 0x8048388
0x0000000d (FINI) 0x804864c
0x00000004 (HASH) 0x8048148
0x00000005 (STRTAB) 0x8048238
0x00000006 (SYMTAB) 0x8048188
0x0000000a (STRSZ) 170 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x8049770
0x00000002 (PLTRELSZ) 48 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x8048358
0x00000011 (REL) 0x8048348
0x00000012 (RELSZ) 16 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffe (VERNEED) 0x80482f8
0x6fffffff (VERNEEDNUM) 2
0x6ffffff0 (VERSYM) 0x80482e2
0x00000000 (NULL) 0x0

Relocation section '.rel.dyn' at offset 0x348 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
0804976c 00000a06 R_386_GLOB_DAT 00000000 __gmon_start__
080497a0 00000305 R_386_COPY 080497a0 stdout

Relocation section '.rel.plt' at offset 0x358 contains 6 entries:
Offset Info Type Sym.Value Sym. Name
0804977c 00000107 R_386_JUMP_SLOT 00000000 fflush
08049780 00000207 R_386_JUMP_SLOT 00000000 pthread_create
08049784 00000407 R_386_JUMP_SLOT 00000000 sleep
08049788 00000507 R_386_JUMP_SLOT 00000000 __libc_start_main
0804978c 00000607 R_386_JUMP_SLOT 00000000 printf
08049790 00000707 R_386_JUMP_SLOT 00000000 pthread_join

There are no unwind sections in this file.

Symbol table '.dynsym' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 293 FUNC GLOBAL DEFAULT UND fflush@GLIBC_2.0 (2)
2: 00000000 396 FUNC GLOBAL DEFAULT UND pthread_create@GLIBC_2.1 (3)
3: 080497a0 4 OBJECT GLOBAL DEFAULT 23 stdout@GLIBC_2.0 (2)
4: 00000000 626 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.0 (2)
5: 00000000 221 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2)
6: 00000000 54 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.0 (2)
7: 00000000 692 FUNC GLOBAL DEFAULT UND pthread_join@GLIBC_2.0 (4)
8: 0804866c 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used
9: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
10: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__

Symbol table '.symtab' contains 76 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08048114 0 SECTION LOCAL DEFAULT 1
2: 08048128 0 SECTION LOCAL DEFAULT 2
3: 08048148 0 SECTION LOCAL DEFAULT 3
4: 08048188 0 SECTION LOCAL DEFAULT 4
5: 08048238 0 SECTION LOCAL DEFAULT 5
6: 080482e2 0 SECTION LOCAL DEFAULT 6
7: 080482f8 0 SECTION LOCAL DEFAULT 7
8: 08048348 0 SECTION LOCAL DEFAULT 8
9: 08048358 0 SECTION LOCAL DEFAULT 9
10: 08048388 0 SECTION LOCAL DEFAULT 10
11: 080483a0 0 SECTION LOCAL DEFAULT 11
12: 08048410 0 SECTION LOCAL DEFAULT 12
13: 0804864c 0 SECTION LOCAL DEFAULT 13
14: 08048668 0 SECTION LOCAL DEFAULT 14
15: 08048684 0 SECTION LOCAL DEFAULT 15
16: 08049688 0 SECTION LOCAL DEFAULT 16
17: 08049690 0 SECTION LOCAL DEFAULT 17
18: 08049698 0 SECTION LOCAL DEFAULT 18
19: 0804969c 0 SECTION LOCAL DEFAULT 19
20: 0804976c 0 SECTION LOCAL DEFAULT 20
21: 08049770 0 SECTION LOCAL DEFAULT 21
22: 08049794 0 SECTION LOCAL DEFAULT 22
23: 080497a0 0 SECTION LOCAL DEFAULT 23
24: 00000000 0 SECTION LOCAL DEFAULT 24
25: 00000000 0 SECTION LOCAL DEFAULT 25
26: 00000000 0 SECTION LOCAL DEFAULT 26
27: 00000000 0 SECTION LOCAL DEFAULT 27
28: 08048434 0 FUNC LOCAL DEFAULT 12 call_gmon_start
29: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
30: 08049688 0 OBJECT LOCAL DEFAULT 16 __CTOR_LIST__
31: 08049690 0 OBJECT LOCAL DEFAULT 17 __DTOR_LIST__
32: 08049698 0 OBJECT LOCAL DEFAULT 18 __JCR_LIST__
33: 0804979c 0 OBJECT LOCAL DEFAULT 22 p.0
34: 080497a4 1 OBJECT LOCAL DEFAULT 23 completed.1
35: 08048458 0 FUNC LOCAL DEFAULT 12 __do_global_dtors_aux
36: 0804848c 0 FUNC LOCAL DEFAULT 12 frame_dummy
37: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
38: 0804968c 0 OBJECT LOCAL DEFAULT 16 __CTOR_END__
39: 08049694 0 OBJECT LOCAL DEFAULT 17 __DTOR_END__
40: 08048684 0 OBJECT LOCAL DEFAULT 15 __FRAME_END__
41: 08049698 0 OBJECT LOCAL DEFAULT 18 __JCR_END__
42: 08048628 0 FUNC LOCAL DEFAULT 12 __do_global_ctors_aux
43: 00000000 0 FILE LOCAL DEFAULT ABS multi.c
44: 0804969c 0 OBJECT GLOBAL DEFAULT 19 _DYNAMIC
45: 08048668 4 OBJECT GLOBAL DEFAULT 14 _fp_hw
46: 00000000 293 FUNC GLOBAL DEFAULT UND fflush@@GLIBC_2.0
47: 00000000 396 FUNC GLOBAL DEFAULT UND pthread_create@@GLIBC_2.1
48: 08049688 0 NOTYPE GLOBAL HIDDEN ABS __fini_array_end
49: 08049798 0 OBJECT GLOBAL HIDDEN 22 __dso_handle
50: 080485e4 66 FUNC GLOBAL DEFAULT 12 __libc_csu_fini
51: 08048388 0 FUNC GLOBAL DEFAULT 10 _init
52: 080497a0 4 OBJECT GLOBAL DEFAULT 23 stdout@@GLIBC_2.0
53: 08048410 0 FUNC GLOBAL DEFAULT 12 _start
54: 00000000 626 FUNC GLOBAL DEFAULT UND sleep@@GLIBC_2.0
55: 08049688 0 NOTYPE GLOBAL HIDDEN ABS __fini_array_start
56: 08048590 82 FUNC GLOBAL DEFAULT 12 __libc_csu_init
57: 080497a0 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
58: 080484b8 123 FUNC GLOBAL DEFAULT 12 main
59: 00000000 221 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
60: 08049688 0 NOTYPE GLOBAL HIDDEN ABS __init_array_end
61: 08049794 0 NOTYPE WEAK DEFAULT 22 data_start
62: 00000000 54 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.0
63: 0804864c 0 FUNC GLOBAL DEFAULT 13 _fini
64: 08049688 0 NOTYPE GLOBAL HIDDEN ABS __preinit_array_end
65: 08048533 91 FUNC GLOBAL DEFAULT 12 print_msg
66: 00000000 692 FUNC GLOBAL DEFAULT UND pthread_join@@GLIBC_2.0
67: 080497a0 0 NOTYPE GLOBAL DEFAULT ABS _edata
68: 08049770 0 OBJECT GLOBAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_
69: 080497a8 0 NOTYPE GLOBAL DEFAULT ABS _end
70: 08049688 0 NOTYPE GLOBAL HIDDEN ABS __init_array_start
71: 0804866c 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used
72: 08049794 0 NOTYPE GLOBAL DEFAULT 22 __data_start
73: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
74: 08049688 0 NOTYPE GLOBAL HIDDEN ABS __preinit_array_start
75: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__

Histogram for bucket list length (total of 3 buckets):
Length Number % of total Coverage
0 0 ( 0.0%)
1 1 ( 33.3%) 10.0%
2 0 ( 0.0%) 10.0%
3 1 ( 33.3%) 40.0%
4 0 ( 0.0%) 40.0%
5 0 ( 0.0%) 40.0%
6 1 ( 33.3%) 100.0%

Version symbols section '.gnu.version' contains 11 entries:
Addr: 00000000080482e2 Offset: 0x0002e2 Link: 4 (.dynsym)
000: 0 (*local*) 2 (GLIBC_2.0) 3 (GLIBC_2.1) 2 (GLIBC_2.0)
004: 2 (GLIBC_2.0) 2 (GLIBC_2.0) 2 (GLIBC_2.0) 4 (GLIBC_2.0)
008: 1 (*global*) 0 (*local*) 0 (*local*)

Version needs section '.gnu.version_r' contains 2 entries:
Addr: 0x00000000080482f8 Offset: 0x0002f8 Link to section: 5 (.dynstr)
000000: Version: 1 File: libpthread.so.0 Cnt: 2
0x0010: Name: GLIBC_2.0 Flags: none Version: 4
0x0020: Name: GLIBC_2.1 Flags: none Version: 3
0x0030: Version: 1 File: libc.so.6 Cnt: 1
0x0040: Name: GLIBC_2.0 Flags: none Version: 2




Bonus for UNIX Admins:

[shan@ipc4 uulp]$ which ps options can do this?

PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
7196 7197 7197 7197 pts/5 7424 Ss 500 0:00 -bash
7197 7415 7415 7197 pts/5 7424 S 500 0:00 \_ ./fork2
7415 7416 7415 7197 pts/5 7424 S 500 0:00 | \_ ./fork2
7416 7417 7415 7197 pts/5 7424 S 500 0:00 | | \_ ./fork2
7417 7419 7415 7197 pts/5 7424 S 500 0:00 | | | \_ ./fork2
7416 7421 7415 7197 pts/5 7424 S 500 0:00 | | \_ ./fork2
7415 7418 7415 7197 pts/5 7424 S 500 0:00 | \_ ./fork2
7418 7420 7415 7197 pts/5 7424 S 500 0:00 | | \_ ./fork2
7415 7422 7415 7197 pts/5 7424 S 500 0:00 | \_ ./fork2
7197 7424 7424 7197 pts/5 7424 R+ 500 0:00 \_ ps ajf


Or this?

7194 7194 7194 ? 00:00:00 sshd
7196 7194 7194 ? 00:00:02 sshd
7197 7197 7197 pts/5 00:00:00 bash
7426 7426 7197 pts/5 00:00:00 fork2
7427 7426 7197 pts/5 00:00:00 fork2
7428 7426 7197 pts/5 00:00:00 fork2
7429 7426 7197 pts/5 00:00:00 fork2
7431 7426 7197 pts/5 00:00:00 fork2
7430 7426 7197 pts/5 00:00:00 fork2
7432 7426 7197 pts/5 00:00:00 fork2
7433 7426 7197 pts/5 00:00:00 fork2
7434 7434 7197 pts/5 00:00:00 ps
2514 2514 2514 ? 00:00:00 xinetd


(answers in whites: ps ajf and ps -ejH )