English to Mongolian translation jobs English to Mongolian localization jobs English to Mongolian translation job
Home Other Translation Jobs Post Your Job - Free! Work for Translation Agencies FAQ

English to Mongolian Translation Jobs



Download 4,000+ Potential Customers for Your Language Services!


English to Mongolian: 1000 words: Job 00001176

Source language: English
Target language: Mongolian

Details of the project: very simple text

Who can apply: Freelancers only

Deadline for applying: 10/10/2009


Delete this ad from the site soon after the deadline for applying

Andy
Alsko
Ukraine
andyvrros at gmail.com

* * * * * * * * * * * * * * * * * *
Posted on Wednesday, 16 Sep 2009, 08:45:05


English to Mongolian language translation job: Job 00001175

Source language: English
Target language: Mongolian

Details of the project: 14.1. Introduction
This chapter covers numerous topics and functions that we lump under the term advanced I/O: nonblocking I/O, record locking, System V STREAMS, I/O multiplexing (the select and poll functions), the readv and writev functions, and memory-mapped I/O (mmap). We need to cover these topics before describing interprocess communication in Chapter 15, Chapter 17, and many of the examples in later chapters.
14.2. Nonblocking I/O
In Section 10.5, we said that the system calls are divided into two categories: the "slow" ones and all the others. The slow system calls are those that can block forever. They include

Reads that can block the caller forever if data isn't present with certain file types (pipes, terminal devices, and network devices)

Writes that can block the caller forever if the data can't be accepted immediately by these same file types (no room in the pipe, network flow control, etc.)

Opens that block until some condition occurs on certain file types (such as an open of a terminal device that waits until an attached modem answers the phone, or an open of a FIFO for writing-only when no other process has the FIFO open for reading)

Reads and writes of files that have mandatory record locking enabled

Certain ioctl operations

Some of the interprocess communication functions (Chapter 15)

We also said that system calls related to disk I/O are not considered slow, even though the read or write of a disk file can block the caller temporarily.

Nonblocking I/O lets us issue an I/O operation, such as an open, read, or write, and not have it block forever. If the operation cannot be completed, the call returns immediately with an error noting that the operation would have blocked.

There are two ways to specify nonblocking I/O for a given descriptor.

If we call open to get the descriptor, we can specify the O_NONBLOCK flag (Section 3.3).

For a descriptor that is already open, we call fcntl to turn on the O_NONBLOCK file status flag (Section 3.14). Figure 3.11 shows a function that we can call to turn on any of the file status flags for a descriptor.

Earlier versions of System V used the flag O_NDELAY to specify nonblocking mode. These versions of System V returned a value of 0 from the read function if there wasn't any data to be read. Since this use of a return value of 0 overlapped with the normal UNIX System convention of 0 meaning the end of file, POSIX.1 chose to provide a nonblocking flag with a different name and different semantics. Indeed, with these older versions of System V, when we get a return of 0 from read, we don't know whether the call would have blocked or whether the end of file was encountered. We'll see that POSIX.1 requires that read return 1 with errno set to EAGAIN if there is no data to read from a nonblocking descriptor. Some platforms derived from System V support both the older O_NDELAY and the POSIX.1 O_NONBLOCK, but in this text, we'll use only the POSIX.1 feature. The older O_NDELAY is for backward compatibility and should not be used in new applications.

4.3BSD provided the FNDELAY flag for fcntl, and its semantics were slightly different. Instead of affecting only the file status flags for the descriptor, the flags for either the terminal device or the socket were also changed to be nonblocking, affecting all users of the terminal or socket, not only the users sharing the same file table entry (4.3BSD nonblocking I/O worked only on terminals and sockets). Also, 4.3BSD returned EWOULDBLOCK if an operation on a nonblocking descriptor could not complete without blocking. Today, BSD-based systems provide the POSIX.1 O_NONBLOCK flag and define EWOULDBLOCK to be the same as EAGAIN. These systems provide nonblocking semantics consistent with other POSIX-compatible systems: changes in file status flags affect all users of the same file table entry, but are independent of accesses to the same device through other file table entries. (Refer to Figures 3.6 and 3.8.)

Example
Let's look at an example of nonblocking I/O. The program in Figure 14.1 reads up to 500,000 bytes from the standard input and attempts to write it to the standard output. The standard output is first set nonblocking. The output is in a loop, with the results of each write being printed on the standard error. The function clr_fl is similar to the function set_fl that we showed in Figure 3.11. This new function simply clears one or more of the flag bits.

If the standard output is a regular file, we expect the write to be executed once:

$ ls -l /etc/termcap print file size
-rw-r--r-- 1 root 702559 Feb 23 2002 /etc/termcap
$ ./a.out < /etc/termcap > temp.file try a regular file first
read 500000 bytes
nwrite = 500000, errno = 0 a single write
$ ls -l temp.file verify size of output file
-rw-rw-r-- 1 sar 500000 Jul 8 04:19 temp.file

But if the standard output is a terminal, we expect the write to return a partial count sometimes and an error at other times. This is what we see:

$ ./a.out < /etc/termcap 2>stderr.out output to terminal
lots of output to terminal ...
$ cat stderr.out
read 500000 bytes
nwrite = 216041, errno = 0
nwrite = -1, errno = 11 1,497 of these errors
...
nwrite = 16015, errno = 0
nwrite = -1, errno = 11 1,856 of these errors
...
nwrite = 32081, errno = 0
nwrite = -1, errno = 11 1,654 of these errors
...
nwrite = 48002, errno = 0
nwrite = -1, errno = 11 1,460 of these errors
...
and so on ...
nwrite = 7949, errno = 0

On this system, the errno of 11 is EAGAIN. The amount of data accepted by the terminal driver varies from system to system. The results will also vary depending on how you are logged in to the system: on the system console, on a hardwired terminal, on network connection using a pseudo terminal. If you are running a windowing system on your terminal, you are also going through a pseudo-terminal device.

Figure 14.1. Large nonblocking write
#include "apue.h"
#include <errno.h>
#include <fcntl.h>

char buf[500000];

int
main(void)
{
int ntowrite, nwrite;
char *ptr;

ntowrite = read(STDIN_FILENO, buf, sizeof(buf));
fprintf(stderr, "read %d bytes\n", ntowrite);

set_fl(STDOUT_FILENO, O_NONBLOCK); /* set nonblocking */

ptr = buf;
while (ntowrite > 0) {
errno = 0;
nwrite = write(STDOUT_FILENO, ptr, ntowrite);
fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);

if (nwrite > 0) {
ptr += nwrite;
ntowrite -= nwrite;
}
}

clr_fl(STDOUT_FILENO, O_NONBLOCK); /* clear nonblocking */

exit(0);
}

In this example, the program issues thousands of write calls, even though only between 10 and 20 are needed to output the data. The rest just return an error. This type of loop, called polling, is a waste of CPU time on a multiuser system. In Section 14.5, we'll see that I/O multiplexing with a nonblocking descriptor is a more efficient way to do this.

Sometimes, we can avoid using nonblocking I/O by designing our applications to use multiple threads (see Chapter 11). We can allow individual threads to block in I/O calls if we can continue to make progress in other threads. This can sometimes simplify the design, as we shall see in Chapter 21; sometimes, however, the overhead of synchronization can add more complexity than is saved from using threads.

14.3. Record Locking
What happens when two people edit the same file at the same time? In most UNIX systems, the final state of the file corresponds to the last process that wrote the file. In some applications, however, such as a database system, a process needs to be certain that it alone is writing to a file. To provide this capability for processes that need it, commercial UNIX systems provide record locking. (In Chapter 20, we develop a database library that uses record locking.)

Record locking is the term normally used to describe the ability of a process to prevent other processes from modifying a region of a file while the first process is reading or modifying that portion of the file. Under the UNIX System, the adjective "record" is a misnomer, since the UNIX kernel does not have a notion of records in a file. A better term is byte-range locking, since it is a range of a file (possibly the entire file) that is locked.

History
One of the criticisms of early UNIX systems was that they couldn't be used to run database systems, because there was no support for locking portions of files. As UNIX systems found their way into business computing environments, various groups added support record locking (differently, of course).

Early Berkeley releases supported only the flock function. This function locks only entire files, not regions of a file.

Record locking was added to System V Release 3 through the fcntl function. The lockf function was built on top of this, providing a simplified interface. These functions allowed callers to lock arbitrary byte ranges in a file, from the entire file down to a single byte within the file.

POSIX.1 chose to standardize on the fcntl approach. Figure 14.2 shows the forms of record locking provided by various systems. Note that the Single UNIX Specification includes lockf in the XSI extension.

Figure 14.2. Forms of record locking supported by various UNIX systems System
Advisory
Mandatory
fcntl
lockf
flock

SUS
?
?
XSI

FreeBSD 5.2.1
?
?
?
?

Linux 2.4.22
?
?
?
?
?

Mac OS X 10.3
?
?
?
?

Solaris 9
?
?
?
?
?


We describe the difference between advisory locking and mandatory locking later in this section. In this text, we describe only the POSIX.1 fcntl locking.

Record locking was originally added to Version 7 in 1980 by John Bass. The system call entry into the kernel was a function named locking. This function provided mandatory record locking and propagated through many versions of System III. Xenix systems picked up this function, and some Intel-based System V derivatives, such as OpenServer 5, still support it in a Xenix-compatibility library.

fcntl Record Locking
Let's repeat the prototype for the fcntl function from Section 3.14.

[View full width]
#include <fcntl.h>

int fcntl(int filedes, int cmd, ... /* struct
flock *flockptr */ );


Returns: depends on cmd if OK (see following), 1 on error


For record locking, cmd is F_GETLK, F_SETLK, or F_SETLKW. The third argument (which we'll call flockptr) is a pointer to an flock structure.

struct flock {
short l_type; /* F_RDLCK, F_WRLCK, or F_UNLCK */
off_t l_start; /* offset in bytes, relative to l_whence */
short l_whence; /* SEEK_SET, SEEK_CUR, or SEEK_END */
off_t l_len; /* length, in bytes; 0 means lock to EOF */
pid_t l_pid; /* returned with F_GETLK */
};

This structure describes

The type of lock desired: F_RDLCK (a shared read lock), F_WRLCK (an exclusive write lock), or F_UNLCK (unlocking a region)

The starting byte offset of the region being locked or unlocked (l_start and l_whence)

The size of the region in bytes (l_len)

The ID (l_pid) of the process holding the lock that can block the current process (returned by F_GETLK only)

There are numerous rules about the specification of the region to be locked or unlocked.

The two elements that specify the starting offset of the region are similar to the last two arguments of the lseek function (Section 3.6). Indeed, the l_whence member is specified as SEEK_SET, SEEK_CUR, or SEEK_END.

Locks can start and extend beyond the current end of file, but cannot start or extend before the beginning of the file.

If l_len is 0, it means that the lock extends to the largest possible offset of the file. This allows us to lock a region starting anywhere in the file, up through and including any data that is appended to the file. (We don't have to try to guess how many bytes might be appended to the file.)

To lock the entire file, we set l_start and l_whence to point to the beginning of the file and specify a length (l_len) of 0. (There are several ways to specify the beginning of the file, but most applications specify l_start as 0 and l_whence as SEEK_SET.)

We mentioned two types of locks: a shared read lock (l_type of F_RDLCK) and an exclusive write lock (F_WRLCK). The basic rule is that any number of processes can have a shared read lock on a given byte, but only one process can have an exclusive write lock on a given byte. Furthermore, if there are one or more read locks on a byte, there can't be any write locks on that byte; if there is an exclusive write lock on a byte, there can't be any read locks on that byte. We show this compatibility rule in Figure 14.3.

Figure 14.3. Compatibility between different lock types

The compatibility rule applies to lock requests made from different processes, not to multiple lock requests made by a single process. If a process has an existing lock on a range of a file, a subsequent attempt to place a lock on the same range by the same process will replace the existing lock with the new one. Thus, if a process has a write lock on bytes 1632 of a file and then tries to place a read lock on bytes 1632, the request will succeed (assuming that we're not racing with any other processes trying to lock the same portion of the file), and the write lock will be replaced by a read lock.

To obtain a read lock, the descriptor must be open for reading; to obtain a write lock, the descriptor must be open for writing.

We can now describe the three commands for the fcntl function.

F_GETLK
Determine whether the lock described by flockptr is blocked by some other lock. If a lock exists that would prevent ours from being created, the information on that existing lock overwrites the information pointed to by flockptr. If no lock exists that would prevent ours from being created, the structure pointed to by flockptr is left unchanged except for the l_type member, which is set to F_UNLCK.

F_SETLK
Set the lock described by flockptr. If we are trying to obtain a read lock (l_type of F_RDLCK) or a write lock (l_type of F_WRLCK) and the compatibility rule prevents the system from giving us the lock (Figure 14.3), fcntl returns immediately with errno set to either EACCES or EAGAIN.

Although POSIX allows an implementation to return either error code, all four implementations described in this text return EAGAIN if the locking request cannot be satisfied.

This command is also used to clear the lock described by flockptr (l_type of F_UNLCK).

F_SETLKW
This command is a blocking version of F_SETLK. (The W in the command name means wait.) If the requested read lock or write lock cannot be granted because another process currently has some part of the requested region locked, the calling process is put to sleep. The process wakes up either when the lock becomes available or when interrupted by a signal.


Be aware that testing for a lock with F_GETLK and then trying to obtain that lock with F_SETLK or F_SETLKW is not an atomic operation. We have no guarantee that, between the two fcntl calls, some other process won't come in and obtain the same lock. If we don't want to block while waiting for a lock to become available to us, we must handle the possible error returns from F_SETLK.

Note that POSIX.1 doesn't specify what happens when one process read-locks a range of a file, a second process blocks while trying to get a write lock on the same range, and a third processes then attempts to get another read lock on the range. If the third process is allowed to place a read lock on the range just because the range is already read-locked, then the implementation might starve processes with pending write locks. This means that as additional requests to read lock the same range arrive, the time that the process with the pending write-lock request has to wait is extended. If the read-lock requests arrive quickly enough without a lull in the arrival rate, then the writer could wait for a long time.

When setting or releasing a lock on a file, the system combines or splits adjacent areas as required. For example, if we lock bytes 100 through 199 and then unlock byte 150, the kernel still maintains the locks on bytes 100 through 149 and bytes 151 through 199. Figure 14.4 illustrates the byte-range locks in this situation.

Figure 14.4. File byte-range lock diagram

If we were to lock byte 150, the system would coalesce the adjacent locked regions into a single region from byte 100 through 199. The resulting picture would be the first diagram in Figure 14.4, the same as when we started.

ExampleRequesting and Releasing a Lock
To save ourselves from having to allocate an flock structure and fill in all the elements each time, the function lock_reg in Figure 14.5 handles all these details.

Since most locking calls are to lock or unlock a region (the command F_GETLK is rarely used), we normally use one of the following five macros, which are defined in apue.h (Appendix B).

#define read_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define readw_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))

We have purposely defined the first three arguments to these macros in the same order as the lseek function.

Figure 14.5. Function to lock or unlock a region of a file
#include "apue.h"
#include <fcntl.h>

int
lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;

lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
lock.l_start = offset; /* byte offset, relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */

return(fcntl(fd, cmd, &lock));
}

ExampleTesting for a Lock
Figure 14.6 defines the function lock_test that we'll use to test for a lock.

If a lock exists that would block the request specified by the arguments, this function returns the process ID of the process holding the lock. Otherwise, the function returns 0 (false). We normally call this function from the following two macros (defined in apue.h):

#define is_read_lockable(fd, offset, whence, len) \
(lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len) \
(lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)

Note that the lock_test function can't be used by a process to see whether it is currently holding a portion of a file locked. The definition of the F_GETLK command states that the information returned applies to an existing lock that would prevent us from creating our own lock. Since the F_SETLK and F_SETLKW commands always replace a process's existing lock if it exists, we can never block on our own lock; thus, the F_GETLK command will never report our own lock.

Figure 14.6. Function to test for a locking condition
#include "apue.h"
#include <fcntl.h>

pid_t
lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;
lock.l_type = type; /* F_RDLCK or F_WRLCK */
lock.l_start = offset; /* byte offset, relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */

if (fcntl(fd, F_GETLK, &lock) < 0)
err_sys("fcntl error");

if (lock.l_type == F_UNLCK)
return(0); /* false, region isn't locked by another proc */
return(lock.l_pid); /* true, return pid of lock owner */
}

ExampleDeadlock
Deadlock occurs when two processes are each waiting for a resource that the other has locked. The potential for deadlock exists if a process that controls a locked region is put to sleep when it tries to lock another region that is controlled by a different process.

Figure 14.7 shows an example of deadlock. The child locks byte 0 and the parent locks byte 1. Then each tries to lock the other's already locked byte. We use the parentchild synchronization routines from Section 8.9 (TELL_xxx and WAIT_xxx) so that each process can wait for the other to obtain its lock. Running the program in Figure 14.7 gives us

$ ./a.out
parent: got the lock, byte 1
child: got the lock, byte 0
child: writew_lock error: Resource deadlock avoided
parent: got the lock, byte 0

When a deadlock is detected, the kernel has to choose one process to receive the error return. In this example, the child was chosen, but this is an implementation detail. On some systems, the child always receives the error. On other systems, the parent always gets the error. On some systems, you might even see the errors split between the child and the parent as multiple lock attempts are made.

Figure 14.7. Example of deadlock detection
#include "apue.h"
#include <fcntl.h>

static void
lockabyte(const char *name, int fd, off_t offset)
{
if (writew_lock(fd, offset, SEEK_SET, 1) < 0)
err_sys("%s: writew_lock error", name);
printf("%s: got the lock, byte %ld\n", name, offset);
}

int
main(void)
{
int fd;
pid_t pid;

/*
* Create a file and write two bytes to it.
*/
if ((fd = creat("templock", FILE_MODE)) < 0)
err_sys("creat error");
if (write(fd, "ab", 2) != 2)
err_sys("write error");

TELL_WAIT();
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
lockabyte("child", fd, 0);
TELL_PARENT(getppid());
WAIT_PARENT();
lockabyte("child", fd, 1);
} else { /* parent */
lockabyte("parent", fd, 1);
TELL_CHILD(pid);
WAIT_CHILD();
lockabyte("parent", fd, 0);
}
exit(0);
}

Implied Inheritance and Release of Locks
Three rules govern the automatic inheritance and release of record locks.

Locks are associated with a process and a file. This has two implications. The first is obvious: when a process terminates, all its locks are released. The second is far from obvious: whenever a descriptor is closed, any locks on the file referenced by that descriptor for that process are released. This means that if we do

fd1 = open(pathname, ...);
read_lock(fd1, ...);
fd2 = dup(fd1);
close(fd2);

after the close(fd2), the lock that was obtained on fd1 is released. The same thing would happen if we replaced the dup with open, as in

fd1 = open(pathname, ...);
read_lock(fd1, ...);
fd2 = open(pathname, ...)
close(fd2);

to open the same file on another descriptor.

Locks are never inherited by the child across a fork. This means that if a process obtains a lock and then calls fork, the child is considered another process with regard to the lock that was obtained by the parent. The child has to call fcntl to obtain its own locks on any descriptors that were inherited across the fork. This makes sense because locks are meant to prevent multiple processes from writing to the same file at the same time. If the child inherited locks across a fork, both the parent and the child could write to the same file at the same time.

Locks are inherited by a new program across an exec. Note, however, that if the close-on-exec flag is set for a file descriptor, all locks for the underlying file are released when the descriptor is closed as part of an exec.

FreeBSD Implementation
Let's take a brief look at the data structures used in the FreeBSD implementation. This should help clarify rule 1, that locks are associated with a process and a file.

Consider a process that executes the following statements (ignoring error returns):

fd1 = open(pathname, ...);
write_lock(fd1, 0, SEEK_SET, 1); /* parent write locks byte 0 */
if ((pid = fork()) > 0) { /* parent */
fd2 = dup(fd1);
fd3 = open(pathname, ...);
} else if (pid == 0) {
read_lock(fd1, 1, SEEK_SET, 1); /* child read locks byte 1 */
}
pause();

Figure 14.8 shows the resulting data structures after both the parent and the child have paused.

Figure 14.8. The FreeBSD data structures for record locking

[View full size image]

We've shown the data structures that result from the open, fork, and dup earlier (Figures 3.8 and 8.2). What is new are the lockf structures that are linked together from the i-node structure. Note that each lockf structure describes one locked region (defined by an offset and length) for a given process. We show two of these structures: one for the parent's call to write_lock and one for the child's call to read_lock. Each structure contains the corresponding process ID.

In the parent, closing any one of fd1, fd2, or fd3 causes the parent's lock to be released. When any one of these three file descriptors is closed, the kernel goes through the linked list of locks for the corresponding i-node and releases the locks held by the calling process. The kernel can't tell (and doesn't care) which descriptor of the three was used by the parent to obtain the lock.

Example
In the program in Figure 13.6, we saw how a daemon can use a lock on a file to ensure that only one copy of the daemon is running. Figure 14.9 shows the implementation of the lockfile function used by the daemon to place a write lock on a file.

Alternatively, we could define the lockfile function in terms of the write_lock function:

#define lockfile(fd) write_lock((fd), 0, SEEK_SET, 0)

Figure 14.9. Place a write lock on an entire file
#include <unistd.h>
#include <fcntl.h>

int
lockfile(int fd)
{
struct flock fl;

fl.l_type = F_WRLCK;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
return(fcntl(fd, F_SETLK, &fl));
}

Locks at End of File
Use caution when locking or unlocking relative to the end of file. Most implementations convert an l_whence value of SEEK_CUR or SEEK_END into an absolute file offset, using l_start and the file's current position or current length. Often, however, we need to specify a lock relative to the file's current position or current length, because we can't call lseek to obtain the current file offset, since we don't have a lock on the file. (There's a chance that another process could change the file's length between the call to lseek and the lock call.)

Consider the following sequence of steps:

writew_lock(fd, 0, SEEK_END, 0);
write(fd, buf, 1);
un_lock(fd, 0, SEEK_END);
write(fd, buf, 1);

This sequence of code might not do what you expect. It obtains a write lock from the current end of the file onward, covering any future data we might append to the file. Assuming that we are at end of file when we perform the first write, that will extend the file by one byte, and that byte will be locked. The unlock that follows has the effect of removing the locks for future writes that append data to the file, but it leaves a lock on the last byte in the file. When the second write occurs, the end of file is extended by one byte, but this byte is not locked. The state of the file locks for this sequence of steps is shown in Figure 14.10

Figure 14.10. File range lock diagram

When a portion of a file is locked, the kernel converts the offset specified into an absolute file offset. In addition to specifying an absolute file offset (SEEK_SET), fcntl allows us to specify this offset relative to a point in the file: current (SEEK_CUR) or end of file (SEEK_END). The kernel needs to remember the locks independent of the current file offset or end of file, because the current offset and end of file can change, and changes to these attributes shouldn't affect the state of existing locks.

If we intended to remove the lock covering the byte we wrote in the first write, we could have specified the length as -1. Negative-length values represent the bytes before the specified offset.

Advisory versus Mandatory Locking
Consider a library of database access routines. If all the functions in the library handle record locking in a consistent way, then we say that any set of processes using these functions to access a database are cooperating processes. It is feasible for these database access functions to use advisory locking if they are the only ones being used to access the database. But advisory locking doesn't prevent some other process that has write permission for the database file from writing whatever it wants to the database file. This rogue process would be an uncooperating process, since it's not using the accepted method (the library of database functions) to access the database.

Mandatory locking causes the kernel to check every open, read, and write to verify that the calling process isn't violating a lock on the file being accessed. Mandatory locking is sometimes called enforcement-mode locking.

We saw in Figure 14.2 that Linux 2.4.22 and Solaris 9 provide mandatory record locking, but FreeBSD 5.2.1 and Mac OS X 10.3 do not. Mandatory record locking is not part of the Single UNIX Specification. On Linux, if you want mandatory locking, you need to enable it on a per file system basis by using the -o mand option to the mount command.

Mandatory locking is enabled for a particular file by turning on the set-group-ID bit and turning off the group-execute bit. (Recall Figure 4.12.) Since the set-group-ID bit makes no sense when the group-execute bit is off, the designers of SVR3 chose this way to specify that the locking for a file is to be mandatory locking and not advisory locking.

What happens to a process that tries to read or write a file that has mandatory locking enabled and the specified part of the file is currently read-locked or write-locked by another process? The answer depends on the type of operation (read or write), the type of lock held by the other process (read lock or write lock), and whether the descriptor for the read or write is nonblocking. Figure 14.11 shows the eight possibilities.

Figure 14.11. Effect of mandatory locking on reads and writes by other processes Type of existing lock on region held by other process
Blocking descriptor, tries to
Nonblocking descriptor, tries to

read
write
read
write

read lock
OK
blocks
OK
EAGAIN

write lock
blocks
blocks
EAGAIN
EAGAIN


In addition to the read and write functions in Figure 14.11, the open function can also be affected by mandatory record locks held by another process. Normally, open succeeds, even if the file being opened has outstanding mandatory record locks. The next read or write follows the rules listed in Figure 14.11. But if the file being opened has outstanding mandatory record locks (either read locks or write locks), and if the flags in the call to open specify either O_TRUNC or O_CREAT, then open returns an error of EAGAIN immediately, regardless of whether O_NONBLOCK is specified.

Only Solaris treats the O_CREAT flag as an error case. Linux allows the O_CREAT flag to be specified when opening a file with an outstanding mandatory lock. Generating the open error for O_TRUNC makes sense, because the file cannot be truncated if it is read-locked or write-locked by another process. Generating the error for O_CREAT, however, makes little sense; this flag says to create the file only if it doesn't already exist, but it has to exist to be record-locked by another process.

This handling of locking conflicts with open can lead to surprising results. While developing the exercises in this section, a test program was run that opened a file (whose mode specified mandatory locking), established a read lock on an entire file, and then went to sleep for a while. (Recall from Figure 14.11 that a read lock should prevent writing to the file by other processes.) During this sleep period, the following behavior was seen in other typical UNIX System programs.

The same file could be edited with the ed editor, and the results written back to disk! The mandatory record locking had no effect at all. Using the system call trace feature provided by some versions of the UNIX System, it was seen that ed wrote the new contents to a temporary file, removed the original file, and then renamed the temporary file to be the original file. The mandatory record locking has no effect on the unlink function, which allowed this to happen.

Under Solaris, the system call trace of a process is obtained by the truss(1) command. FreeBSD and Mac OS X use the ktrace(1) and kdump(1) commands. Linux provides the strace(1) command for tracing the system calls made by a process.

The vi editor was never able to edit the file. It could read the file's contents, but whenever we tried to write new data to the file, EAGAIN was returned. If we tried to append new data to the file, the write blocked. This behavior from vi is what we expect.

Using the Korn shell's > and >> operators to overwrite or append to the file resulted in the error "cannot create."

Using the same two operators with the Bourne shell resulted in an error for >, but the >> operator just blocked until the mandatory lock was removed, and then proceeded. (The difference in the handling of the append operator is because the Korn shell opens the file with O_CREAT and O_APPEND, and we mentioned earlier that specifying O_CREAT generates an error. The Bourne shell, however, doesn't specify O_CREAT if the file already exists, so the open succeeds but the next write blocks.)

Results will vary, depending on the version of the operating system you are using. The bottom line with this exercise is to be wary of mandatory record locking. As seen with the ed example, it can be circumvented.

Mandatory record locking can also be used by a malicious user to hold a read lock on a file that is publicly readable. This can prevent anyone from writing to the file. (Of course, the file has to have mandatory record locking enabled for this to occur, which may require the user be able to change the permission bits of the file.) Consider a database file that is world readable and has mandatory record locking enabled. If a malicious user were to hold a read lock on the entire file, the file could not be written to by other processes.

Example
The program in Figure 14.12 determines whether mandatory locking is supported by a system.

This program creates a file and enables mandatory locking for the file. The program then splits into parent and child, with the parent obtaining a write lock on the entire file. The child first sets its descriptor nonblocking and then attempts to obtain a read lock on the file, expecting to get an error. This lets us see whether the system returns EACCES or EAGAIN. Next, the child rewinds the file and tries to read from the file. If mandatory locking is provided, the read should return EACCES or EAGAIN (since the descriptor is nonblocking). Otherwise, the read returns the data that it read. Running this program under Solaris 9 (which supports mandatory locking) gives us

$ ./a.out temp.lock
read_lock of already-locked region returns 11
read failed (mandatory locking works): Resource temporarily unavailable

If we look at either the system's headers or the intro(2) manual page, we see that an errno of 11 corresponds to EAGAIN. Under FreeBSD 5.2.1, we get

$ ./a.out temp.lock
read_lock of already-locked region returns 35
read OK (no mandatory locking), buf = ab

Here, an errno of 35 corresponds to EAGAIN. Mandatory locking is not supported.

Figure 14.12. Determine whether mandatory locking is supported
#include "apue.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>

int
main(int argc, char *argv[])
{
int fd;
pid_t pid;
char buf[5];
struct stat statbuf;
if (argc != 2) {
fprintf(stderr, "usage: %s filename\n", argv[0]);
exit(1);
}
if ((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0)
err_sys("open error");
if (write(fd, "abcdef", 6) != 6)
err_sys("write error");

/* turn on set-group-ID and turn off group-execute */
if (fstat(fd, &statbuf) < 0)
err_sys("fstat error");
if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)
err_sys("fchmod error");

TELL_WAIT();

if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid > 0) { /* parent */
/* write lock entire file */
if (write_lock(fd, 0, SEEK_SET, 0) < 0)
err_sys("write_lock error");

TELL_CHILD(pid);

if (waitpid(pid, NULL, 0) < 0)
err_sys("waitpid error");
} else { /* child */
WAIT_PARENT(); /* wait for parent to set lock */

set_fl(fd, O_NONBLOCK);

/* first let's see what error we get if region is locked */
if (read_lock(fd, 0, SEEK_SET, 0) != -1) /* no wait */
err_sys("child: read_lock succeeded");
printf("read_lock of already-locked region returns %d\n",
errno);

/* now try to read the mandatory locked file */
if (lseek(fd, 0, SEEK_SET) == -1)
err_sys("lseek error");
if (read(fd, buf, 2) < 0)
err_ret("read failed (mandatory locking works)");
else
printf("read OK (no mandatory locking), buf = %2.2s\n",
buf);
}
exit(0);
}

Example
Let's return to the first question of this section: what happens when two people edit the same file at the same time? The normal UNIX System text editors do not use record locking, so the answer is still that the final result of the file corresponds to the last process that wrote the file.

Some versions of the vi editor use advisory record locking. Even if we were using one of these versions of vi, it still doesn't prevent users from running another editor that doesn't use advisory record locking.

If the system provides mandatory record locking, we could modify our favorite editor to use it (if we have the sources). Not having the source code to the editor, we might try the following. We write our own program that is a front end to vi. This program immediately calls fork, and the parent just waits for the child to complete. The child opens the file specified on the command line, enables mandatory locking, obtains a write lock on the entire file, and then executes vi. While vi is running, the file is write-locked, so other users can't modify it. When vi terminates, the parent's wait returns, and our front end terminates.

A small front-end program of this type can be written, but it doesn't work. The problem is that it is common for most editors to read their input file and then close it. A lock is released on a file whenever a descriptor that references that file is closed. This means that when the editor closes the file after reading its contents, the lock is gone. There is no way to prevent this in the front-end program.

We'll use record locking in Chapter 20 in our database library to provide concurrent access to multiple processes. We'll also provide some timing measurements to see what effect record locking has on a process.
14.4. STREAMS
The STREAMS mechanism is provided by System V as a general way to interface communication drivers into the kernel. We need to discuss STREAMS to understand the terminal interface in System V, the use of the poll function for I/O multiplexing (Section 14.5.2), and the implementation of STREAMS-based pipes and named pipes (Sections 17.2 and 17.2.1).

Be careful not to confuse this usage of the word stream with our previous usage of it in the standard I/O library (Section 5.2). The streams mechanism was developed by Dennis Ritchie [Ritchie 1984] as a way of cleaning up the traditional character I/O system (c-lists) and to accommodate networking protocols. The streams mechanism was later added to SVR3, after enhancing it a bit and capitalizing the name. Complete support for STREAMS (i.e., a STREAMS-based terminal I/O system) was provided with SVR4. The SVR4 implementation is described in [AT&T 1990d]. Rago [1993] discusses both user-level STREAMS programming and kernel-level STREAMS programming.

STREAMS is an optional feature in the Single UNIX Specification (included as the XSI STREAMS Option Group). Of the four platforms discussed in this text, only Solaris provides native support for STREAMS. A STREAMS subsystem is available for Linux, but you need to add it yourself. It is not usually included by default.

A stream provides a full-duplex path between a user process and a device driver. There is no need for a stream to talk to a hardware device; a stream can also be used with a pseudo-device driver. Figure 14.13 shows the basic picture for what is called a simple stream.

Figure 14.13. A simple stream

Beneath the stream head, we can push processing modules onto the stream. This is done using an ioctl command. Figure 14.14 shows a stream with a single processing module. We also show the connection between these boxes with two arrows to stress the full-duplex nature of streams and to emphasize that the processing in one direction is separate from the processing in the other direction.

Figure 14.14. A stream with a processing module

Any number of processing modules can be pushed onto a stream. We use the term push, because each new module goes beneath the stream head, pushing any previously pushed modules down. (This is similar to a last-in, first-out stack.) In Figure 14.14, we have labeled the downstream and upstream sides of the stream. Data that we write to a stream head is sent downstream. Data read by the device driver is sent upstream.

STREAMS modules are similar to device drivers in that they execute as part of the kernel, and they are normally link edited into the kernel when the kernel is built. If the system supports dynamically-loadable kernel modules (as do Linux and Solaris), then we can take a STREAMS module that has not been link edited into the kernel and try to push it onto a stream; however, there is no guarantee that arbitrary combinations of modules and drivers will work properly together.

We access a stream with the functions from Chapter 3: open, close, read, write, and ioctl. Additionally, three new functions were added to the SVR3 kernel to support STREAMS (getmsg, putmsg, and poll), and another two (getpmsg and putpmsg) were added with SVR4 to handle messages with different priority bands within a stream. We describe these five new functions later in this section.

The pathname that we open for a stream normally lives beneath the /dev directory. Simply looking at the device name using ls -l, we can't tell whether the device is a STREAMS device. All STREAMS devices are character special files.

Although some STREAMS documentation implies that we can write processing modules and push them willy-nilly onto a stream, the writing of these modules requires the same skills and care as writing a device driver. Generally, only specialized applications or functions push and pop STREAMS modules.

Before STREAMS, terminals were handled with the existing c-list mechanism. (Section 10.3.1 of Bach [1986] and Section 10.6 of McKusick et al. [1996] describe c-lists in SVR2 and 4.4BSD, respectively.) Adding other character-based devices to the kernel usually involved writing a device driver and putting everything into the driver. Access to the new device was typically through the raw device, meaning that every user read or write ended up directly in the device driver. The STREAMS mechanism cleans up this way of interacting, allowing the data to flow between the stream head and the driver in STREAMS messages and allowing any number of intermediate processing modules to operate on the data.

STREAMS Messages
All input and output under STREAMS is based on messages. The stream head and the user process exchange messages using read, write, ioctl, getmsg, getpmsg, putmsg, and putpmsg. Messages are also passed up and down a stream between the stream head, the processing modules, and the device driver.

Between the user process and the stream head, a message consists of a message type, optional control information, and optional data. We show in Figure 14.15 how the various message types are generated by the arguments to write, putmsg, and putpmsg. The control information and data are specified by strbuf structures:

struct strbuf
int maxlen; /* size of buffer */
int len; /* number of bytes currently in buffer */
char *buf; /* pointer to buffer */
};

Figure 14.15. Type of STREAMS message generated for write, putmsg, and putpmsg Function
Control?
Data?
band
flag
Message type generated

write
N/A
yes
N/A
N/A
M_DATA (ordinary)

putmsg
no
no
N/A
0
no message sent, returns 0

putmsg
no
yes
N/A
0
M_DATA (ordinary)

putmsg
yes
yes or no
N/A
0
M_PROTO (ordinary)

putmsg
yes
yes or no
N/A
RS_HIPRI
M_PCPROTO (high-priority)

putmsg
no
yes or no
N/A
RS_HIPRI
error, EINVAL

putpmsg
yes or no
yes or no
0255
0
error, EINVAL

putpmsg
no
no
0255
MSG_BAND
no message sent, returns 0

putpmsg
no
yes
0
MSG_BAND
M_DATA (ordinary)

putpmsg
no
yes
1255
MSG_BAND
M_DATA (priority band)

putpmsg
yes
yes or no
0
MSG_BAND
M_PROTO (ordinary)

putpmsg
yes
yes or no
1255
MSG_BAND
M_PROTO (priority band)

putpmsg
yes
yes or no
0
MSG_HIPRI
M_PCPROTO (high-priority)

putpmsg
no
yes or no
0
MSG_HIPRI
error, EINVAL

putpmsg
yes or no
yes or no
nonzero
MSG_HIPRI
error, EINVAL


When we send a message with putmsg or putpmsg, len specifies the number of bytes of data in the buffer. When we receive a message with getmsg or getpmsg, maxlen specifies the size of the buffer (so the kernel won't overflow the buffer), and len is set by the kernel to the amount of data stored in the buffer. We'll see that a zero-length message is OK and that a len of 1 can specify that there is no control or data.

Why do we need to pass both control information and data? Providing both allows us to implement service interfaces between a user process and a stream. Olander, McGrath, and Israel [1986] describe the original implementation of service interfaces in System V. Chapter 5 of AT&T [1990d] describes service interfaces in detail, along with a simple example. Probably the best-known service interface, described in Chapter 4 of Rago [1993], is the System V Transport Layer Interface (TLI), which provides an interface to the networking system.

Another example of control information is sending a connectionless network message (a datagram). To send the message, we need to specify the contents of the message (the data) and the destination address for the message (the control information). If we couldn't send control and data together, some ad hoc scheme would be required. For example, we could specify the address using an ioctl, followed by a write of the data. Another technique would be to require that the address occupy the first N bytes of the data that is written using write. Separating the control information from the data, and providing functions that handle both (putmsg and getmsg) is a cleaner way to handle this.

There are about 25 different types of messages, but only a few of these are used between the user process and the stream head. The rest are passed up and down a stream within the kernel. (These message types are of interest to people writing STREAMS processing modules, but can safely be ignored by people writing user-level code.) We'll encounter only three of these message types with the functions we use (read, write, getmsg, getpmsg, putmsg, and putpmsg):

M_DATA (user data for I/O)

M_PROTO (protocol control information)

M_PCPROTO (high-priority protocol control information)

Every message on a stream has a queueing priority:

High-priority messages (highest priority)

Priority band messages

Ordinary messages (lowest priority)

Ordinary messages are simply priority band messages with a band of 0. Priority band messages have a band of 1255, with a higher band specifying a higher priority. High-priority messages are special in that only one is queued by the stream head at a time. Additional high-priority messages are discarded when one is already on the stream head's read queue.

Each STREAMS module has two input queues. One receives messages from the module above (messages moving downstream from the stream head toward the driver), and one receives messages from the module below (messages moving upstream from the driver toward the stream head). The messages on an input queue are arranged by priority. We show in Figure 14.15 how the arguments to write, putmsg, and putpmsg cause these various priority messages to be generated.

There are other types of messages that we don't consider. For example, if the stream head receives an M_SIG message from below, it generates a signal. This is how a terminal line discipline module sends the terminal-generated signals to the foreground process group associated with a controlling terminal.

putmsg and putpmsg Functions
A STREAMS message (control information or data, or both) is written to a stream using either putmsg or putpmsg. The difference in these two functions is that the latter allows us to specify a priority band for the message.

[View full width]
#include <stropts.h>

int putmsg(int filedes, const struct strbuf *ctlptr,
const struct strbuf *dataptr, int flag);

int putpmsg(int filedes, const struct strbuf *ctlptr,
const struct strbuf *dataptr, int band
, int flag);


Both return: 0 if OK, 1 on error


We can also write to a stream, which is equivalent to a putmsg without any control information and with a flag of 0.

These two functions can generate the three different priorities of messages: ordinary, priority band, and high priority. Figure 14.15 details the combinations of the arguments to these two functions that generate the various types of messages.

The notation "N/A" means not applicable. In this figure, a "no" for the control portion of the message corresponds to either a null ctlptr argument or ctlptr>len being 1. A "yes" for the control portion corresponds to ctlptr being non-null and ctlptr>len being greater than or equal to 0. The data portion of the message is handled equivalently (using dataptr instead of ctlptr).

STREAMS ioctl Operations
In Section 3.15, we said that the ioctl function is the catchall for anything that can't be done with the other I/O functions. The STREAMS system continues this tradition.

Between Linux and Solaris, there are almost 40 different operations that can be performed on a stream using ioctl. Most of these operations are documented in the streamio(7) manual page. The header <stropts.h> must be included in C code that uses any of these operations. The second argument for ioctl, request, specifies which of the operations to perform. All the requests begin with I_. The third argument depends on the request. Sometimes, the third argument is an integer value; sometimes, it's a pointer to an integer or a structure.

Exampleisastream Function
We sometimes need to determine if a descriptor refers to a stream or not. This is similar to calling the isatty function to determine if a descriptor refers to a terminal device (Section 18.9). Linux and Solaris provide the isastream function.

#include <stropts.h>

int isastream(int filedes);


Returns: 1 (true) if STREAMS device, 0 (false) otherwise


Like isatty, this is usually a trivial function that merely tries an ioctl that is valid only on a STREAMS device. Figure 14.16 shows one possible implementation of this function. We use the I_CANPUT ioctl command, which checks if the band specified by the third argument (0 in the example) is writable. If the ioctl succeeds, the stream is not changed.

We can use the program in Figure 14.17 to test this function.

Running this program on Solaris 9 shows the various errors returned by the ioctl function:

$ ./a.out /dev/tty /dev/fb /dev/null /etc/motd
/dev/tty: streams device
/dev/fb: not a stream: Invalid argument
/dev/null: not a stream: No such device or address
/etc/motd: not a stream: Inappropriate ioctl for device

Note that /dev/tty is a STREAMS device, as we expect under Solaris. The character special file /dev/fb is not a STREAMS device, but it supports other ioctl requests. These devices return EINVAL when the ioctl request is unknown. The character special file /dev/null does not support any ioctl operations, so the error ENODEV is returned. Finally, /etc/motd is a regular file, not a character special file, so the classic error ENOTTY is returned. We never receive the error we might expect: ENOSTR ("Device is not a stream").

The message for ENOTTY used to be "Not a typewriter," a historical artifact because the UNIX kernel returns ENOTTY whenever an ioctl is attempted on a descriptor that doesn't refer to a character special device. This message has been updated on Solaris to "Inappropriate ioctl for device."

Figure 14.16. Check if descriptor is a STREAMS device
#include <stropts.h>
#include <unistd.h>

int
isastream(int fd)
{
return(ioctl(fd, I_CANPUT, 0) != -1);
}

Figure 14.17. Test the isastream function
#include "apue.h"
#include <fcntl.h>

int
main(int argc, char *argv[])
{
int i, fd;

for (i = 1; i < argc; i++) {
if ((fd = open(argv[i], O_RDONLY)) < 0) {
err_ret("%s: can't open", argv[i]);
continue;
}

if (isastream(fd) == 0)
err_ret("%s: not a stream", argv[i]);
else
err_msg("%s: streams device", argv[i]);
}

exit(0);
}

Example
If the ioctl request is I_LIST, the system returns the names of all the modules on the streamthe ones that have been pushed onto the stream, including the topmost driver. (We say topmost because in the case of a multiplexing driver, there may be more than one driver. Chapter 12 of Rago [1993] discusses multiplexing drivers in detail.) The third argument must be a pointer to a str_list structure:

struct str_list {
int sl_nmods; /* number of entries in array */
struct str_mlist *sl_modlist; /* ptr to first element of array */
};

We have to set sl_modlist to point to the first element of an array of str_mlist structures and set sl_nmods to the number of entries in the array:

struct str_mlist {
char l_name[FMNAMESZ+1]; /* null terminated module name */
};

The constant FMNAMESZ is defined in the header <sys/conf.h> and is often 8. The extra byte in l_name is for the terminating null byte.

If the third argument to the ioctl is 0, the count of the number of modules is returned (as the value of ioctl) instead of the module names. We'll use this to determine the number of modules and then allocate the required number of str_mlist structures.

Figure 14.18 illustrates the I_LIST operation. Since the returned list of names doesn't differentiate between the modules and the driver, when we print the module names, we know that the final entry in the list is the driver at the bottom of the stream.

If we run the program in Figure 14.18 from both a network login and a console login, to see which STREAMS modules are pushed onto the controlling terminal, we get the following:

$ who
sar console May 1 18:27
sar pts/7 Jul 12 06:53
$ ./a.out /dev/console
#modules = 5
module: redirmod
module: ttcompat
module: ldterm
module: ptem
driver: pts
$ ./a.out /dev/pts/7
#modules = 4
module: ttcompat
module: ldterm
module: ptem
driver: pts

The modules are the same in both cases, except that the console has an extra module on top that helps with virtual console redirection. On this computer, a windowing system was running on the console, so /dev/console actually refers to a pseudo terminal instead of to a hardwired device. We'll return to the pseudo terminal case in Chapter 19.

Figure 14.18. List the names of the modules on a stream
#include "apue.h"
#include <fcntl.h>
#include <stropts.h>
#include <sys/conf.h>

int
main(int argc, char *argv[])
{
int fd, i, nmods;
struct str_list list;

if (argc != 2)
err_quit("usage: %s <pathname>", argv[0]);

if ((fd = open(argv[1], O_RDONLY)) < 0)
err_sys("can't open %s", argv[1]);
if (isastream(fd) == 0)
err_quit("%s is not a stream", argv[1]);

/*
* Fetch number of modules.
*/
if ((nmods = ioctl(fd, I_LIST, (void *) 0)) < 0)
err_sys("I_LIST error for nmods");
printf("#modules = %d\n", nmods);

/*
* Allocate storage for all the module names.
*/
list.sl_modlist = calloc(nmods, sizeof(struct str_mlist));
if (list.sl_modlist == NULL)
err_sys("calloc error");
list.sl_nmods = nmods;

/*
* Fetch the module names.
*/
if (ioctl(fd, I_LIST, &list) < 0)
err_sys("I_LIST error for list");

/*
* Print the names.
*/
for (i = 1; i <= nmods; i++)
printf(" %s: %s\n", (i == nmods) ? "driver" : "module",
list.sl_modlist++->l_name);

exit(0);
}

write to STREAMS Devices
In Figure 14.15 we said that a write to a STREAMS device generates an M_DATA message. Although this is generally true, there are some additional details to consider. First, with a stream, the topmost processing module specifies the minimum and maximum packet sizes that can be sent downstream. (We are unable to query the module for these values.) If we write more than the maximum, the stream head normally breaks the data into packets of the maximum size, with one final packet that can be smaller than the maximum.

The next thing to consider is what happens if we write zero bytes to a stream. Unless the stream refers to a pipe or FIFO, a zero-length message is sent downstream. With a pipe or FIFO, the default is to ignore the zero-length write, for compatibility with previous versions. We can change this default for pipes and FIFOs using an ioctl to set the write mode for the stream.

Write Mode
Two ioctl commands fetch and set the write mode for a stream. Setting request to I_GWROPT requires that the third argument be a pointer to an integer, and the current write mode for the stream is returned in that integer. If request is I_SWROPT, the third argument is an integer whose value becomes the new write mode for the stream. As with the file descriptor flags and the file status flags (Section 3.14), we should always fetch the current write mode value and modify it rather than set the write mode to some absolute value (possibly turning off some other bits that were enabled).

Currently, only two write mode values are defined.

SNDZERO
A zero-length write to a pipe or FIFO will cause a zero-length message to be sent downstream. By default, this zero-length write sends no message.

SNDPIPE
Causes SIGPIPE to be sent to the calling process that calls either write or putmsg after an error has occurred on a stream.


A stream also has a read mode, and we'll look at it after describing the getmsg and getpmsg functions.

getmsg and getpmsg Functions
STREAMS messages are read from a stream head using read, getmsg, or getpmsg.

[View full width]
#include <stropts.h>

int getmsg(int filedes, struct strbuf *restrict
ctlptr,
struct strbuf *restrict dataptr, int
*restrict flagptr);

int getpmsg(int filedes, struct strbuf *restrict
ctlptr,
struct strbuf *restrict dataptr, int
*restrict bandptr,
int *restrict flagptr);


Both return: non-negative value if OK, 1 on error


Note that flagptr and bandptr are pointers to integers. The integer pointed to by these two pointers must be set before the call to specify the type of message desired, and the integer is also set on return to the type of message that was read.

If the integer pointed to by flagptr is 0, getmsg returns the next message on the stream head's read queue. If the next message is a high-priority message, the integer pointed to by flagptr is set to RS_HIPRI on return. If we want to receive only high-priority messages, we must set the integer pointed to by flagptr to RS_HIPRI before calling getmsg.

A different set of constants is used by getpmsg. We can set the integer pointed to by flagptr to MSG_HIPRI to receive only high-priority messages. We can set the integer to MSG_BAND and then set the integer pointed to by bandptr to a nonzero priority value to receive only messages from that band, or higher (including high-priority messages). If we only want to receive the first available message, we can set the integer pointed to by flagptr to MSG_ANY; on return, the integer will be overwritten with either MSG_HIPRI or MSG_BAND, depending on the type of message received. If the message we retrieved was not a high-priority message, the integer pointed to by bandptr will contain the message's priority band.

If ctlptr is null or ctlptr>maxlen is 1, the control portion of the message will remain on the stream head's read queue, and we will not process it. Similarly, if dataptr is null or dataptr>maxlen is 1, the data portion of the message is not processed and remains on the stream head's read queue. Otherwise, we will retrieve as much control and data portions of the message as our buffers will hold, and any remainder will be left on the head of the queue for the next call.

If the call to getmsg or getpmsg retrieves a message, the return value is 0. If part of the control portion of the message is left on the stream head read queue, the constant MORECTL is returned. Similarly, if part of the data portion of the message is left on the queue, the constant MOREDATA is returned. If both control and data are left, the return value is (MORECTL|MOREDATA).

Read Mode
We also need to consider what happens if we read from a STREAMS device. There are two potential problems.

What happens to the record boundaries associated with the messages on a stream?

What happens if we call read and the next message on the stream has control information?

The default handling for condition 1 is called byte-stream mode. In this mode, a read takes data from the stream until the requested number of bytes has been read or until there is no more data. The message boundaries associated with the STREAMS messages are ignored in this mode. The default handling for condition 2 causes the read to return an error if there is a control message at the front of the queue. We can change either of these defaults.

Using ioctl, if we set request to I_GRDOPT, the third argument is a pointer to an integer, and the current read mode for the stream is returned in that integer. A request of I_SRDOPT takes the integer value of the third argument and sets the read mode to that value. The read mode is specified by one of the following three constants:

RNORM
Normal, byte-stream mode (the default), as described previously.

RMSGN
Message-nondiscard mode. A read takes data from a stream until the requested number of bytes have been read or until a message boundary is encountered. If the read uses a partial message, the rest of the data in the message is left on the stream for a subsequent read.

RMSGD
Message-discard mode. This is like the nondiscard mode, but if a partial message is used, the remainder of the message is discarded.


Three additional constants can be specified in the read mode to set the behavior of read when it encounters messages containing protocol control information on a stream:

RPROTNORM
Protocol-normal mode: read returns an error of EBADMSG. This is the default.

RPROTDAT
Protocol-data mode: read returns the control portion as data.

RPROTDIS
Protocol-discard mode: read discards the control information but returns any data in the message.


Only one of the message read modes and one of the protocol read modes can be set at a time. The default read mode is (RNORM|RPROTNORM).

Example
The program in Figure 14.19 is the same as the one in Figure 3.4, but recoded to use getmsg instead of read.

If we run this program under Solaris, where both pipes and terminals are implemented using STREAMS, we get the following output:

$ echo hello, world | ./a.out requires STREAMS-based pipes
flag = 0, ctl.len = -1, dat.len = 13
hello, world
flag = 0, ctl.len = 0, dat.len = 0 indicates a STREAMS hangup
$ ./a.out requires STREAMS-based terminals
this is line 1
flag = 0, ctl.len = -1, dat.len = 15
this is line 1
and line 2
flag = 0, ctl.len = -1, dat.len = 11
and line 2
^D type the terminal EOF character
flag = 0, ctl.len = -1, dat.len = 0 tty end of file is not the same as a hangup
$ ./a.out < /etc/motd
getmsg error: Not a stream device

When the pipe is closed (when echo terminates), it appears to the program in Figure 14.19 as a STREAMS hangup, with both the control length and the data length set to 0. (We discuss pipes in Section 15.2.) With a terminal, however, typing the end-of-file character causes only the data length to be returned as 0. This terminal end of file is not the same as a STREAMS hangup. As expected, when we redirect standard input to be a non-STREAMS device, getmsg returns an error.

Figure 14.19. Copy standard input to standard output using getmsg
#include "apue.h"
#include <stropts.h>

#define BUFFSIZE 4096

int
main(void)
{
int n, flag;
char ctlbuf[BUFFSIZE], datbuf[BUFFSIZE];
struct strbuf ctl, dat;

ctl.buf = ctlbuf;
ctl.maxlen = BUFFSIZE;
dat.buf = datbuf;
dat.maxlen = BUFFSIZE;
for ( ; ; ) {
flag = 0; /* return any message */
if ((n = getmsg(STDIN_FILENO, &ctl, &dat, &flag)) < 0)
err_sys("getmsg error");
fprintf(stderr, "flag = %d, ctl.len = %d, dat.len = %d\n",
flag, ctl.len, dat.len);
if (dat.len == 0)
exit(0);
else if (dat.len > 0)
if (write(STDOUT_FILENO, dat.buf, dat.len) != dat.len)
err_sys("write error");
}
}


Special requirements to the applicants: It's a language to Mongolian language converting!!! Help me?

Who can apply: Freelancers only

Deadline for applying: 05/12/2009


Keep this ad at the site permanently

Delgerdalai
USI school
Mongolia
deegii_107 at yahoo.com

* * * * * * * * * * * * * * * * * *
Posted on Tuesday, 12 May 2009, 05:34:18


English to Mongolian translation job: Job 00001174

Source language: English
Target language: Mongolian

Dear Translators,

We are now expanding our resource pool and setting up a team to handle English to Mongolian translation needs for a client. The projects from this client are about Cell phone localization.

If you are interested, please send me your CV together with your rate per source character.

Jane Zhao
Shenzhen EastSun Translations Co., Ltd.
www.eastsuntranslation.com
email: job at eastsuntranslation.com

Special requirements to the applicants:
1) must be Mongolian native speakers;
2) have 3 over years? translation experience;
3) can use Trados and SDLX,
4) be familar with cell phone localization,
5) willing to accept a translation test.

Who can apply: Freelancers and agencies

Deadline for applying: April/30/2009


Keep this ad at the site permanently

Jane Zhao
Shenzhen EastSun Translations Co., Ltd.
China
job at eastsuntranslation.com

www.eastsuntranslation.com
* * * * * * * * * * * * * * * * * *
Posted on Saturday, 18 Apr 2009, 05:41:24


Lionbridge Internet Assessors in Mongolia Wanted: Job 00001173

Source language: English
Target language: Mongolian

Details of the project:

Lionbridge Internet Assessor
Type: Part-time, Freelance
Duration: On-going

Lionbridge Technologies, Inc. (Nasdaq: LIOX) is a leading provider of globalization and testing services. Based in Waltham, Mass., Lionbridge now maintains more than 50 solution centers in 25 countries and provides services under the Lionbridge and VeriTest brands. To learn more, visit http://www.lionbridge.com  

The team at Lionbridge Technologies with solution centres in 25 countries worldwide is recruiting part-time self-employed workers who are fluent speakers in Mongolian and English who are based in Mongolia to join its team of Internet Assessors.

Requirements:
- Proficiency in English is essential
- Fluency in Mongolian is essential
- We are seeking people who have access to the internet, and are confident in using the Internet
- Background in IT is helpful but not essential
- For cultural and historical awareness purposes you must be resident in Mongolia for 5 consecutive years

The main aim of the work is to improve the quality of a search engine?s results for all web users worldwide. The work involves evaluating results of a web search, for their appropriateness to the search query input. You will be required to provide feedback i.e. your opinion of the result displayed.

The hours are flexible to fit around your family and home life, and the position is ideal for someone looking for a work-life balance (10-20 hours per week).

Please apply by following the url or copying it into your browser:
https://e-recruit.sap.lionbridge.com:8010

Alternatively if you can not use the above link, please send a copy of your CV in English only to: mn.raters.bal at lionbridge.com


Who can apply: Freelancers only

Deadline for applying: 12/31/2009

Delete this ad from the site soon after the deadline for applying

Mary Malone
Lionbridge Technologies, Inc.
Ireland
mn.raters.bal at lionbridge.com
http://www.lionbridge.com 
* * * * * * * * * * * * * * * * * *
Posted on Wednesday, 04 Mar 2009, 10:01:42
 


Lionbridge вэб үнэлгээний мэргэжилтэн Ажил: хагас орон тооны: Job 00001172

Source language: English
Target language: Mongolian

Lionbridge вэб үнэлгээний мэргэжилтэн
Ажил: хагас орон тооны
Хугацаа: хугацаагаар хязгаарлагдахгүй

Lionbridge Technologies, Inc. (Nasdaq: LIOX) буюу Лайонбриж текноложиес инк /хөрөнгийн биржийн LIOX кодтой/ компани нь олон улсын сүлжээнийн туршилтын үйлчилгээ үзүүлдэг тэргүүлэх нэгж юм. АНУ-ын Массачусетс мужийн Волтам /Waltham/ хотод төвтэй, 25 орон дахь 50 гаруй нэгжүүдээрээ дамжуулан Lionbridge ба VeriTest брэнд нэрээр ажиллаж байна. Нэмэлт мэдээллийг
http://www.lionbridge.com  

25 орнод салбартай Лайонбриж текноложиес-ийн баг нь монгол, англи хэлийг сайн мэддэг, хувиараа ажилладаг, Монголд оршин суудаг хүмүүсийг вэбийн үнэлэгчийн ажилд хагас орон тоогоор авна.

Шалгуур:
- Англи хэл дээр чөлөөтэй ажиллах чадвартай байх нь нэн чухал
- Монгол хэлэндээ сайн
- Интернэтийг сайн мэддэг, байнга Интернэт дээр байх боломжтой
- Мэдээллийн технологийн мэргэжилтэй бол зохимжтой, гэхдээ заавал биш

Вэбийн хайлтийн системийг үнэлэх ажил хийх юм. Ажиллах цаг чөлөөтэй, гэр бүл, ажлынхаа хуваарьт тааруулан, долоо хоногт 10-20 цаг нэмж ажиллах сонирхолтой хүн байвал хамгийн тохиромжтой.

Өргөлдөө: mn.raters.bal at lionbridge.com-руу англи хэл дээр илгээх, үүнд товч намтар, мөн гарал үүсэл, оршин сууж буй газар, ур чадвар, сонирхол, ажлын туршлагаа тоймлосон өргөдөл ирүүлэх. .


Who can apply: Freelancers only

Deadline for applying: 12/31/2009

Delete this ad from the site soon after the deadline for applying

Mary Malone
Lionbridge Technologies, Inc.
Ireland
mn.raters.bal at lionbridge.com
http://www.lionbridge.com
* * * * * * * * * * * * * * * * * *
Posted on Tuesday, 17 Feb 2009, 16:33:09


Lionbridge Internet Assessors in Mongolia Wanted: Job 00001171

Source language: English
Target language: Mongolian

Details of the project: Lionbridge Internet Assessor
Type: Part-time /Freelance
Duration: On-going

Lionbridge Technologies, Inc. (Nasdaq: LIOX) is a leading provider of globalization and testing services. Based in Waltham, Mass., Lionbridge now maintains more than 50 solution centers in 25 countries and provides services under the Lionbridge and VeriTest brands.
To learn more, visit
http://www.lionbridge.com

The team at Lionbridge Technologies with solution centres in 25 countries worldwide is recruiting part-time self-employed workers who are fluent speakers in Mongolian and English who are based in Mongolia to join its team of Internet Assessors

Requirements:
- Good working knowledge of English is essential
- Fluency in Mongolian
- We are seeking people who have access to the internet, and confident in using the Internet.
- Background in IT is helpful but not essential
- For cultural and historical awareness purposes you must be resident in Mongolia for 5 consecutive years.

The main aim of the work is to improve a search engine?s results for all web users worldwide. The work involves evaluating results of a web search, for their appropriateness to the search query input. You will be required to provide feedback i.e. your opinion of the result displayed.
The hours are flexible to fit around your family and home life, so ideal for someone looking for a work-life balance (10-20 hours per week).

Please apply to: mn.raters.bal at lionbridge.com with a copy of your CV in ENGLISH ONLY and a cover letter detailing your relevant skill set, hobbies, interests and experiences as well as your nationality and country of residence.


Who can apply: Freelancers only

Deadline for applying: 12/31/2009

Delete this ad from the site soon after the deadline for applying

Mary Malone
Lionbridge Technologies, Inc.
Ireland
mn.raters.bal at lionbridge.com
http://www.lionbridge.com
* * * * * * * * * * * * * * * * * *
Posted on Tuesday, 17 Feb 2009, 16:31:33


Translation Jobs in Other Language Pairs

Other Translation Jobs


If you can't find here the job announcement you have been notified about or you had seen here before, that means the job has been closed already or the posting was spam or another unacceptable material.


Post your translation job - Free!

Translation agencies are welcome to register here - Free!

Freelance translators are welcome to register here - Free!

Subscribe to TranslationDirectory.com newsletter - Free!





 
Web www.TranslationDirectory.com



Free Newsletter


Subscribe to our free newsletter to receive news and updates from us:

 
← ↓ Advertisements





christianity portal

translation jobs

Copyright © 2003-2009 by TranslationDirectory.com
Legal Disclaimer
Site Map