System Services Guide
只搜尋這本書
以 PDF 格式下載這本書

Programming in the System Environment

1

This chapter introduces the C language functions for handling errors, processes, and signals. It also describes the following tools and gives you a sense of the situations in which you use these tools and how the tools fit together:
  • File and record locking
  • Interprocess communication
  • Virtual memory
  • Process scheduling

Programming Functions

The SunOS 5.x functions discussed in this section are the interface between the kernel and the user programs. The read, write, and other functions in Sections 2 and 3 of the Solaris 2.4 Reference Manual AnswerBook define the SunOS operating system.
Strictly speaking, these functions are the only way to access such facilities as the file system, interprocess communication primitives, and multitasking mechanisms.
When you use the library routines described in section 3 of the Solaris 2.4 Reference Manual AnswerBook, the details of their implementation are transparent to the program. For example, the function read underlies the fread implementation in the standard C library. In contrast, programs that
call these functions directly are generally portable only to other SunOS 5.x or SunOS 5.x-like systems. Other operations, however, including most multitasking mechanisms, require direct interaction with the system kernel. These operations are the subject of the first part of this chapter.
A C program is automatically linked with the functions invoked when you compile the program. The procedure might be different for programs written in other languages. See the Linker and Libraries Guide for more information.

Error Handling

Functions that do not conclude successfully almost always return a value of -1 to your program. (For a few functions in Section 2 of the man Pages(2): System Calls, there are a few calls for which no return value is defined, but these are the exceptions.) In addition to the -1 that is returned to the program, the unsuccessful function places an integer in an externally declared variable, errno. In a C program, you can determine the value in errno if your program contains the following statement
:

Text Box(503x37)

The value in errno is not cleared on successful calls, so check it only if the function returned -1. See error descriptions in intro(2) of the man Pages(2): System Calls.
You can use the C language function perror(3C) to print an error message on stderr based on the value of errno.

Basic File I/O

These functions perform basic operations on files:
Table 1-1
Function NamePurpose
openOpen a file for reading or writing
closeClose a file descriptor
Table 1-1
readRead from a file
writeWrite to a file
creatCreate a new file or rewrite an existing one
unlinkRemove a directory entry
lseekMove read/write file pointer

Advanced File I/O

These functions create and remove directories and files, create links to existing files, and obtain or modify file status information:
Table 1-2
Function NamePurpose
linkLink to a file
accessDetermine accessibility of a file
mknodMake a special or ordinary file
chmodChange mode of file
chown
lchown
fchown
Change owner and group of a file
utimeSet file access and modification times
stat
lstat
fstat
Get file status
fcntlPerform file control functions
ioctlControl device
fpathconf
pathconf
Get configurable path name variables
opendir
readdir
closedir
Perform directory operations
mkdirMake a directory
readlinkRead the value of a symbolic link
Table 1-2
Function NamePurpose
renameChange the name of a file
rmdirRemove a directory
symlinkMake a symbolic link to a file

Terminal I/O

These functions deal with a general terminal interface for controlling asynchronous communications ports:
Table 1-3
Function NamePurpose
tcgetattr
tcsetattr
Get and set terminal attributes
tcsendbreak tcdrain tcflush tcflowPerform line control functions
cfgetospeed
cfgetispeed
cfsetispeed
cfsetospeed
Get and set baud rate
tcgetpgrp
tcsetpgrp
Get and set terminal foreground process group ID
tcgetsidGet terminal session ID

Processes

These functions control user processes:
Table 1-4
Function NamePurpose
forkCreate a new process
exec
execl
execv
execle
execve
execlp
execvp
Execute a program
exit
_exit
Terminate a process
waitWait for a child process to stop or terminate
setuid
setgid
Set user and group IDs
setpgrpSet process group ID
chdir
fchdir
Change working directory
chrootChange root directory
niceChange priority of a process
getcontext
setcontext
Get and set current user context
getgroups
setgroups
Get or set supplementary group access list IDs
getpid
getpgrp
getppid
getpgid
Get process, process group, and parent process IDs
getuid
geteuid
getgid
getegid
Get real user, effective user, real group, and effective group IDs
Table 1-4
Function NamePurpose
pauseSuspend process until signal
priocntlControl process scheduler
setpgidSet process group ID
setsidSet session ID
waitidWait for a child process to change state
killSend a signal to a process or group of processes

Overview of Processes

Whenever you execute a command, you start a process that is numbered and tracked by the operating system. A flexible feature of the operating system is that processes can be generated by other processes. This happens often.
For example, log in to your system running the shell, then use an editor such as vi. Take the option of invoking the shell from vi. Execute the ps command and you will see a display resembling this (which shows the results of a ps -f command):

Text Box(342x72)

Here, user abc has four processes active. When you trace the chain shown in the process ID (PID) and parent process ID (PPID) columns, you see that the shell that was started when user abc logged on is process 24210; its parent is the initialization process (process ID 1). Process 24210 is the parent of process 24631, and so on.
The four processes in the example are shell-level commands, but you can start new processes from your own program.
Overlooking the case where your program is interactive and contains many choices for the user, it might need to run one or more other programs based on conditions it encounters in its own processing. The reasons why it might not be practical to create one large executable include:
  • The load module might get too big to fit in the maximum process size for your system.
  • You might not have control over the object code of all the other modules you want to include.
With the exec(2) and fork(2) functions, discussed in the following sections, you can stop one process and start another, or you can start a copy of a process.

exec(2)

exec is the name of a family of functions that includes execl, execv, execle, execve, execlp, and execvp. They all transform the calling process into a new process, but with different ways of pulling together and presenting the arguments of the function. For example, execl could be used like this:

         execl("/usr/bin/prog2", "prog2", progarg1, progarg2, (char (*)0);  

The execl argument list is:
/usr/bin/prog2              The path name of the new process file.
prog2                       The name the new process gets in its argv[0].
progarg1, progarg2          The arguments to prog2 as char (*)s.
(char (*)0)                 A null char pointer to mark the end of the 
                            arguments.

See exec(2) for more details.
The key point about the exec family is that there is no return from a successful execution; the new process overlays the process that makes the exec call. The new process also takes over the process ID and other attributes of the old process. If the call to exec is unsuccessful, control is returned to your program with a return value of -1. You can check errno to learn why it failed.

fork(2)

The fork call creates a new process that is an exact copy of the calling process. The new process is known as the child process; the creator is known as the parent process. The one major difference between the two processes is that the child gets its own unique process ID. When the fork process has finished successfully, it returns a 0 to the child process and the child's process ID to the parent. Although the two processes are identical, you can differentiate between them:
  • Because the return value is different between the child process and the parent, a program can contain the logic to determine different paths.
  • The child process issues an exec for an entirely different program.
  • The parent process issues a wait until it is notified that the process being exec'd by the child process is finished.
Your code might include statements like this:

Text Box(500x318)

Because the child process ID is taken over by the new exec'd process, the parent knows the ID. This is a way of leaving one program to run another, returning to the point in the first program where processing left off. This is basically what the function system in the standard C library does.
Keep in mind that the fragment of code above includes minimal checking for error conditions. There is also potential confusion about open files and which program is writing to a file.
Leaving out the possibility of named files, the new process created by the fork or exec function has the three standard files that are automatically opened: stdin, stdout, and stderr. When the parent has buffered output that should appear before output from the child, the buffers must be flushed before the fork.
Also, if the parent and the child process both read input from a stream, whatever is read by one process will be lost to the other. That is, once something has been delivered from the input buffer to a process, the pointer has moved on.

Basic Interprocess Communication

The pipe(2) and dup(2) functions connect processes so they can communicate.
pipe is the function for creating an interprocess channel. (The interprocess channel created by pipe is not suitable for interprocessor communication. STREAMS supports mounting a fifo.)
dup is the function for duplicating an open file descriptor.

Advanced Interprocess Communication

These functions support interprocess messages, semaphores, and shared memory and are useful in database management. (These IPC mechanisms do not apply to processes on separate hosts.)
Table 1-5
Function NamePurpose
msggetGet message queue
msgctlPerform message control operations
msgsndSend a message
msgopReceive a message
semgetGet set of semaphores
semctlControl semaphore operations
semopPerform semaphore operations
Table 1-5
Function NamePurpose
shmgetGet shared memory segment identifier
shmctlControl shared memory operations
shmntAttach shared memory segment
shmdtDetach shared memory segment

Memory Management

These functions give you access to virtual memory facilities:
Table 1-6
Function NamePurpose
getpagesizeGet system page size
memcntlControl memory management
mmapMap pages of memory
mprotectSet protection of memory mapping
munmapUnmap pages of memory
plockLock process, text, or data in memory
brk
sbrk
Change data segment space allocation

File System Control

These functions allow you to control various aspects of the file system:
Table 1-7
Function NamePurpose
ustatGet file system statistics
syncUpdate super block
mountMount a file system
unmountUnmount a file system
Table 1-7
Function NamePurpose
statvfs
fstatvfs
Get file system information
sysfsGet file system type information

Signals Overview

The system defines a set of signals that can be delivered to a process. Signal delivery resembles the occurrence of a hardware interrupt: the signal is normally blocked from further occurrence, the current process context is saved, and a new one is built. A process can specify the handler to which a signal is delivered or specify that the signal is to be blocked or ignored. A process can also specify that an action is to be taken when signals occur.
Some signals cause a process to exit when they are not caught. This can be accompanied by creation of a core image file, containing the current memory image of the process for use in postmortem debugging. A process can choose to have signals delivered on a particular stack, so that sophisticated software stack manipulations are possible.
Not all signals have the same priority. If multiple signals are simultaneously pending and deliverable, the signal with the smallest number will be delivered first. A signal routine usually executes concurrently with the signal that caused its invocation, but other signals can still occur. Mechanisms are provided so that critical sections of code can protect themselves against the occurrence of specified signals.
Each signal defined by the system falls into one of five classes:
  • Hardware conditions
  • Software conditions
  • Input/output notification
  • Process control
  • Resource control
The set of signals is defined in the header <signal.h>.

Hardware Signals

Hardware signals are derived from exceptional conditions that can occur during execution. Such signals include
  • SIGFPE--representing floating point and other arithmetic exceptions
  • SIGILL-- for illegal instruction execution
  • SIGSEGV--for addresses outside the currently assigned area of memory or for accesses that violate memory protection constraints
  • SIGBUS--for accesses that result in hardware-related errors
Other, more CPU-specific hardware signals exist, such as SIGIOT, SIGEMT, and SIGTRAP.

Software Signals

Software signals reflect interrupts generated by user request:
  • SIGINT--the normal interrupt signal
  • SIGQUIT--this more powerful quit signal usually causes a core image to be generated
  • SIGHUP and SIGTERM--these signals provide graceful process termination, either because a user has "hung up" or through a user or program request
  • SIGKILL--a more powerful termination signal that a process cannot catch or ignore
  • SIGUSR1 and SIGUSR2--allow programs to define their own asynchronous events
  • SIGRTMIN through SIGRTMAX--a range of signals which allow programs to define their own events
Other software signals (SIGALRM, SIGVTALRM, SIGPROF) indicate the expiration of interval timers.

Notification Signals

A process can request notification with a SIGPOLL signal when input or output is possible on a descriptor, or when an operation finishes.
A process can request to receive a SIGURG signal when an urgent condition arises on a communication channel.

Process Control Signals

A process can be notified by a signal sent to it or to the members of its process group.
  • SIGSTOP--stops the process; this powerful signal cannot be caught
  • SIGTSTP--indicates that a user request stopped the process
  • SIGTTIN--indicates that an input request stopped the process
  • SIGTTOU--indicates that an output request stopped the process
  • SIGCONT--indicates that a process continued from a stopped state
  • SIGCHLD--notifies a process that a child process has changed state, either by stopping or by terminating

Resource Limit Signals

Exceeding resource limits can generate signals.
  • SIGXCPU occurs when a process nears its CPU time limit
  • SIGXFSZ warns that the limit on file-size creation has been reached

Signal Handlers

A process has a handler associated with each signal. The handler controls the way the signal is delivered.
Each handler specifies an interrupt routine for the signal, that the signal is to be ignored, or that a default action (usually process termination) takes place if the signal occurs. The constants SIG_IGN and SIG_DFL, used as values for sa_handler, cause ignoring or defaulting of a condition.

Note - To reset a signal handler from within a signal handler, reset the signal handler routine that catches the signal (signal(n, SIG_DFL);) and unblock the blocked signal with sigprocmask.

Signal Set Operations The sa_mask field specifies the set of signals to be masked when the handler is invoked; it implicitly includes the signal that invoked the handler.
Five operations are permitted on signal sets.
  • sigemptyset--empties the signal set
  • sigfillset--fills the signal set with every signal currently supported
  • sigaddset--adds specific signals to the set
  • sigdelset--deletes specific signals from the set
  • sigismember--tests set membership
Initialize signal sets with a call to sigemptyset or sigfillset.
Unique Signal Properties The sa_flags field specifies unique properties of the signal. Such properties include:
  • whether or not functions should be restarted if the signal handler returns
  • whether the signal action should be reset to SIG_DFL when it is caught
  • whether subsequent occurences of a signal which is already pending should be queued
  • whether the handler should operate on the normal run-time stack or on a particular signal stack.
If osa is nonzero, the previous signal action is returned.
Signal Generation A process can send a signal to another process or group of processes with the calls:

  #include <signal.h>  
  
  int  
  kill(pid_t pid, int sig);  


  #include <signal.h>  
  
  int  
  sigsend(idtype_t idtype, id_t id, int sig);                     int  
  
  int  
  sigsendset(procset_t *psp, int sig);  


  #include <signal.h>  
  
  int  
  sigqueue (pid_t pid, int signo, const union sigval value);  

Unless the process sending the signal is privileged, its real or effective user ID must be that of the receiving process's real or saved user ID.
Signals can also be sent from a terminal device to the process group or session leader associated with the terminal. See the termio(7)manual page for more information.
Signal Delivery When a signal condition arises for a process, the signal is added to a set of signals pending for the process. If the signal is not currently blocked by the process then it will be delivered.
The process of signal delivery:
  • Adds the signal to be delivered and those signals specified in the associated signal handler's sa_mask to a set of those masked for the process
  • Saves the current process context
  • Places the process in the context of the signal handling routine
The call is arranged so that if the signal handling routine exits normally the signal mask is restored and the process resumes execution in the original context.

Note - For the process to resume in a different context it must arrange to restore the signal mask itself.

The mask of blocked signals is independent of handlers for delays. It delays the delivery of signals in the same way that a raised hardware interrupt priority level delays hardware interrupts. Preventing an interrupt from occurring by changing the handler is like disabling a device from further interrupts.
The signal handling routine sa_handler is called by a C call of the form:

  #include <siginfo.h>  
  #include <ucontext.h>  
  
  (*sa_handler)(int signo, siginfo_t *infop, ucontext_t *ucp);  

The signo field gives the number of the signal that occurred. The infop field is either equal to 0 or points to a structure that contains information detailing the reason the signal was generated. This information must be explicitly asked for when the signal action is specified. The ucp field is a pointer to a structure containing the process's context before delivery of the signal. It restores the process's context upon return from the signal handler.
To block a section of code against one or more signals, use a sigprocmask call to add a set of signals to the existing mask and to return the old mask:

  #include <signal.h>  
  
  int  
  sigprocmask(int SIG_BLOCK,const sigset_t *mask,sigset_t *omask);  

The old mask can then be restored later with sigprocmask:

  #include <signal.h>  
  
  int  
  sigprocmask(int SIG_UNBLOCK, const sigset_t *mask,  
      sigset_t *omask);  

Or, the old mask can be reset with

  #include <signal.h>  
  
  int  
  sigprocmask(int SIG_SETMASK, const sigset_t *mask,  
      sigset_t *omask);  

The sigprocmask call can be used to read the current mask without changing it by specifying a null pointer as its mask argument.
You can check conditions with some signals blocked, and then pause to wait for a signal and restore the mask, by using:

  #include <signal.h>  
  
  int  
  sigsuspend(const sigset_t *mask);  

Applications can receive signals synchronously by using:

  #include <signal.h>  
  
  int  
  sigwaitinfo(const sigset_t *mask, siginfo_t *siginfo);  
  
  int  
  sigtimedwait(const sigset_t *mask,siginfo_t *siginfo,  
      const struct timespec *timeout);  

Programs maintaining complex or fixed-size stacks can use the call:

  #include <signal.h>  
  
  int  
  sigaltstack(const stack_t *ss, stack_t *oss);  

where the stack_t structure contains

       int *ss_sp  
       long ss_size  
       int ss_flags  

This provides the system with a stack based at ss_sp of size ss_size for signal delivery. The system automatically adjusts for direction of stack growth. ss_flags indicates whether the process is currently on the signal stack and whether or not the signal stack is disabled.
When a signal is to be delivered and the process has requested that it be delivered on the alternate stack (see sigaction above), the system checks whether the process is on a signal stack. If it is not, then the process is switched to the signal stack for delivery, with the return from the signal arranged to restore the previous stack.
For a process to take a nonlocal exit from the signal routine, or to run code from the signal stack that uses a different stack, use a sigaltstack call to reset the signal stack.
Signal functions include:
Table 1-8
Function NamePurpose
sigaction
sigset
sighold
sigrelse
sigignore
Manage signal (detailed)
sigaltstackSet or get signal alternate stack context
signal
sigpause
Manage signal (simplified)
sigpendingExamine signals that are blocked and pending
sigprocmaskChange or examine signal mask
killSend a signal to a process or group of processes
sigsend
sigsendset
Send a signal to a process or group of processes
Table 1-8
Function NamePurpose
sigqueueSend a signal with a value to a process
sigwaitinfosigti
sigtimedwait
Receive a value and signal synchronously
sigsuspendInstall a signal mask and suspend process until signal

Miscellaneous Functions

These functions are for administration, timing, and other miscellaneous purposes:
Table 1-9
Function NamePurpose
ulimitGet and set user limits
alarmSet a process alarm clock
getmsgGet next message off a stream
getrlimit
setrlimit
Control maximum system resource consumption
unameGet/set name of current system
putmsgSend a message on a stream
profilGet execution time profile
sysconfDetermine value for system configuration
uadminPerform administrative control
timeGet time
stimeSet time
acctEnable or disable process accounting.

Note - When changing file descriptor limits with setrlimit, note that some library routines allocate data structures based on the current file descriptor list, so continually resetting the limit throughout the life of the process can cause problems. This is especially true for some SunOS 4.x applications running under the BCP (Binary Compatibility Package), which accept a maximum of
256 open file descriptors. When you increase the file descriptor limit, it is good practice to increase it at the beginning of the process and to decrease it after forking, but before exec'ing, the new process.

Developing Software

This section briefly describes tools for input, processing, and output.

File and Record Locking

You lock files, or portions of files, to prevent the errors that can occur when two or more users of a file try to update information at the same time.
File locking and record locking are really the same thing, except that file locking implies that the whole file is affected, while record locking means that only a specified portion of the file is locked. (In the SunOS 5.x system, file structure is undefined: a record is a concept of the programs that use the file.)

Read and Write Locks

Two types of locks are available: read locks and write locks. When a process places a read lock on a file, other processes can also read the file but all are prevented from writing to it--that is, changing any of the data. When a process places a write lock on a file, no other processes can read or write in the file until the lock is removed. Write locks are also known as "exclusive locks." The term "shared lock" is sometimes applied to read locks.

Mandatory and Advisory Locking

Mandatory locking means that the discipline is enforced automatically for the functions that read, write, or create files. This is done through a permission flag established by the file's owner (or the superuser) and enforced by the kernel.
Advisory locking means that the processes that use the file take the responsibility for setting and removing locks as needed.
The principal weakness in mandatory locking is that the lock is in place only while the single function is being called. It is extremely common for a single transaction to require a series of reads and writes before it is complete. In cases like this, this transaction must be viewed as an indivisible unit.
The preferred way to manage locking in this case is to make certain the lock is in place before any I/O starts, and that it is not removed until the transaction is done. Advisory locking would be more appropriate than mandatory locking in this instance.
Also see the fcntl(2), fcntl(5), lockf(3C), and chmod(2) manual pages. The fcntl(2) function performs file and record locking (although it isn't limited to that only). The fcntl(5) page describes the file control options. The subroutine lockf(3C) can also be used to lock sections of a file or an entire file. The chmod(2) function is used to set or clear mandatory locking mode.

Interprocess Communications

Pipes, named pipes, and signals are all forms of interprocess communication. The SunOS 5.x system offers three additional facilities for interprocess communications (IPC):
messagesCommunication is in the form of data stored in a buffer. The buffer can be either sent or received.
semaphoresCommunication is in the form of positive integers with a value between 0 and 32,767. Semaphores may be contained in an array the size of which is determined by the system administrator. The default maximum size for the array is 25.
shared memoryCommunication takes place through a common area of main memory. One or more processes can attach a segment of memory and as a consequence can share whatever data is placed there.
The following sets of IPC functions are described in Section 2 of the man Pages(2): System Calls:
msgget      semget      shmget
msgctl      semctl      shmctl
msgop       semop       shmop

Each "get" function returns to the calling program an identifier for the type of IPC facility that is being requested.
The "ctl" functions provide a variety of control operations that include obtaining (IPC_STAT), setting (IPC_SET), and removing (IPC_RMID) the values in data structures associated with the identifiers picked up by the get calls.
The "op" manual pages describe calls that are used to perform the particular operations characteristic of the type of IPC facility being used. The msgop page describes calls that send or receive messages. The semop operations increment or decrement the value of a semaphore, among other functions. The shmop operations attach or detach shared memory segments.
For more information, see section 2 of the man Pages(2): System Calls.

Process Scheduler

The system scheduler determines when processes run. It maintains process priorities based on configuration parameters, process behavior, and user requests; it uses these priorities to assign processes to the CPU.
Scheduler functions give users absolute control over the order in which certain processes run and the amount of time each process may use the CPU before another process gets a chance.
By default, the scheduler uses a time-sharing policy. A time-sharing policy adjusts process priorities dynamically in an attempt to give good response time to interactive processes and good throughput to CPU-intensive processes.
The scheduler offers an alternate real-time scheduling policy as well. Real-time scheduling allows users to set fixed priorities-- priorities that the system does not change. The highest priority real-time user process always gets the CPU as soon as it can be run, even if other system processes are also eligible to be run. A program can therefore specify the exact order in which processes run. You can also write a program so that its real-time processes have a guaranteed response time from the system.
For most SunOS 5.x system environments, the default scheduler configuration works well and no real-time processes are needed: administrators need not change configuration parameters and users need not change scheduler
properties of their processes. However, for some programs with strict timing constraints, real-time processes are the only way to guarantee that the timing requirements are met.
For more information, see priocntl(1), priocntl(2) and dispadmin(1M) of the man Pages(2): System Calls.

Memory Management

The operating system includes a complete set of memory-mapping mechanisms. Process address spaces are composed of a vector of memory pages, each of which can be independently mapped and manipulated. The memory-management facilities:
  • Unify system operations on memory
  • Provide a set of kernel mechanisms powerful and general enough to support the implementation of fundamental system services without special-purpose kernel support
  • Maintain consistency with the existing environment, in particular using the file system as the name space for named virtual-memory objects
The system virtual memory consists of all available physical memory resources including local and remote file systems, processor primary memory, swap space, and other random-access devices. Named objects in the virtual memory are referenced though the file system. However, not all file system objects are in the virtual memory; devices that the SunOS operating system cannot treat as storage, such as terminal and network device files, are not in the virtual memory. Some virtual memory objects, such as private process memory and shared memory segments, do not have names.

The Memory Mapping Interface

You can access to the facilities of the virtual memory system through several sets of functions:
  • mmap establishes a mapping between the process address space and a virtual memory object
  • mprotect assigns access protection to a block of virtual memory
  • munmap removes a memory mapping
  • getpagesize returns the system-dependent size of a memory page
  • mincore tells whether mapped memory pages are in primary memory

Note - It is better to use the memory management routines to implement shared memory than to use the advanced interprocess communication functions.

For more information, see the mmap(2), mprotect(2), munmap(2), getpagesize(3B), and mincore(2)of the man Pages(2): System Calls.