•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 (e.g., no room in the pipe, network flow control)
•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)
1. If we call open to get the descriptor, we can specify the O_NONBLOCK flag
2. For a descriptor that is already open, we call fcntl to turn on the O_NONBLOCK file status flag (Section 3.14). Figure3.12 shows a function that we can call to turn on any of the file status flags for a descriptor.
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <myerr.h> #define BUFFSIZE 50 void set_fl(int fd,int flags) { int val; if((val = fcntl(fd,F_GETFL,0)) < 0) { err_sys("fcntl F_GETFL error\n"); } val |= flags; if(fcntl(fd,F_SETFL,val) < 0) { err_sys("fcntl F_SETEL error\n"); } } void clr_fl(int fd,int flags) { int val; if((val = fcntl(fd,F_GETFL,0)) < 0) { err_sys("fcntl F_GETFL error\n"); } val &= ~flags; if(fcntl(fd,F_SETFL,val) < 0) { err_sys("fcntl F_SETEL error\n"); } } char buf[BUFFSIZE]; int main() { 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); 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); exit(0); }
#include <fcntl.h> int fcntl(int fd ,int cmd ,... /* struct flock *flockptr */ ); Returns: depends oncmd if OK (see following), −1 on error
struct flock { short int l_type; /* Type oflock: F_RDLCK, F_WRLCK, or F_UNLCK. */ short int l_whence; /* Where `l_start' is relative to (like`lseek'). */ #ifndef __USE_FILE_OFFSET64 __off_t l_start; /* Offsetwhere the lock begins. */ __off_t l_len; /* Size of thelocked area; zero means until EOF. */ #else __off64_t l_start; /* Offsetwhere the lock begins. */ __off64_t l_len; /* Size of thelocked area; zero means until EOF. */ #endif __pid_t l_pid; /* Processholding the lock. */ };
Numerous rules apply to 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_lenis 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 .)
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. Further more, if there are one or more read locks
on a byte, there can’t be any write locks on that byte;
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.
Set the lock described by flockptr.If we a re t rying 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(Figur e14.3), fcntl re turns immediately with errno set to either EACCES or EAGAIN
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.
/*********************************************************************************************** code write : EOF code date : 2014.04.08 e-mail: [email protected] code purpose : Just a demo for deadlock ************************************************************************************************/ #include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <myerr.h> #include <signal.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; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; return fcntl(fd,cmd,&lock); } static void lockabyte(const char *name,int fd,off_t offset) { if(lock_reg(fd,F_SETLKW,F_WRLCK,offset,SEEK_SET,1) < 0) { err_sys("%s: lock_reg error\n",name); } printf("%s got the lock,byte %ld\n",name,offset); } void parent_handler(int signo) { if(waitpid(-1,NULL,0) < 0) { printf("waitpid error\n"); } } void child_handler(int signo) { /* Nothing need to be done....child should not to wait for parent's termination */ } int main() { int fd; pid_t pid; //-----------------------initialize the signal handler------------------------------------- struct sigaction sa_parent,sa_child; sigemptyset(&sa_parent.sa_mask); sa_parent.sa_flags = 0; sa_parent.sa_handler = parent_handler; sigemptyset(&sa_child.sa_mask); sa_child.sa_flags = 0; sa_child.sa_handler = child_handler; if(sigaction(SIGUSR1,&sa_parent,NULL) < 0) { printf("parent sigaction error\n"); return 0; } if(sigaction(SIGUSR2,&sa_child,NULL) < 0) { printf("child sigaction error\n"); return 0; } //----------------------------------------------------------------------------------------- if((fd = creat("./templock",(S_IRUSR|S_IWUSR|S_IXUSR))) < 0) { err_sys("creat error\n"); } if(write(fd,"ab",2) != 2) { err_sys("write error\n"); } if((pid = fork()) < 0) { err_sys("fork error\n"); } else if(pid == 0) { lockabyte("child",fd,0); kill(getppid(),SIGUSR1);// TELL_PARENT( ); lockabyte("child",fd,1); } else { lockabyte("parent",fd,1); kill(pid,SIGUSR2);// TELL_CHILD(pid); lockabyte("parent",fd,0); } exit(0); }
/**************************************************************************** code writer : EOF code date : 2014.04.08 e-mail : [email protected] code purpose: Just a demonstartion for mandatory locking If there is something wrong with my code, please touch me by e-mail. ****************************************************************************/ #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <sys/wait.h> #include <stdlib.h> #include <myerr.h> void set_fl(int fd,int flags) { int val; if((val = fcntl(fd,F_GETFL,0)) < 0) { err_sys("fcntl F_GETFL error\n"); } val |= flags; if(fcntl(fd,F_SETFL,val) < 0) { err_sys("fcntl F_SETEL error\n"); } } int lock_reg(int fd,int cmd,int type,off_t offset,int whence,off_t len) { struct flock lock; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; return fcntl(fd,cmd,&lock); } void child_handler(int signo) { //Nothing to be done. Just a empty handler } 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,S_IRUSR | S_IWUSR | S_IXUSR)) < 0) { err_sys("open error\n"); return 0; } if(write(fd,"abcdef",6) != 6) { err_sys("write error\n"); return 0; } if(fstat(fd,&statbuf) < 0) { err_sys("fstat error\n"); } if(fchmod(fd,(statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0) { err_sys("fchmod error\n"); return 0; } if((pid = fork()) < 0) { err_sys("for error\n"); } else if(pid > 0) { //write lock if(lock_reg(fd,F_SETLK,F_WRLCK,0,SEEK_SET,6) < 0) { printf("lock_reg error\n"); } sleep(3); //It's neccessay to sleep for a few second to //let the child install the signal handler first. kill(pid,SIGUSR1); if(waitpid(pid,NULL,0) < 0 ) { err_sys("waitpid error\n"); } } else { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = child_handler; sigaddset(&sa.sa_mask,SIGUSR1); if(sigaction(SIGUSR1,&sa,NULL) < 0) { printf("sigaction error\n"); return 0; } pause();//wait to caught the signal set_fl(fd,O_NONBLOCK); //read lock if(lock_reg(fd,F_SETLK,F_RDLCK,0,SEEK_SET,6) < 0) { printf("lock_reg error\n"); } printf("read lock of alredy-locked region returns %d \n",errno); if(lseek(fd,0,SEEK_SET) < 0) { err_sys("lseek error\n"); } if(read(fd,buf,2) < 0) { err_ret("read failed (mandatory locking works)\n"); } else { printf("read OK (no mandatory locking),buf = %2.2s\n",buf); } return 0; } exit(0); }
我只能说操蛋。。。linux根本就不支持STREAMS。。。。这是第三版和第二版最大的不同!
APUE preface的原话:
•Linux doesn’tinclude STREAMS in its base system, although packages (LiS and OpenSS7 ) areavailable to add this functionality.
好吧下面是我已经做好的笔记。。。
All input and output under STREAMS is based on messages. Thestream 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 thestream head, the processing modules, and the device driver.
linux下面找到的strbuf的定义
struct strbuf { int maxlen; /* Maximum buffer length. */ int len; /* Length ofdata. */ char *buf; /* Pointer tobuffer. */ };
We'll encounter only three of these message types with the functions we use (read, write, getmsg, getpmsg, putmsg, and putpmsg):
- M_DATA (userdata 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.
#include <stropts.h>
int putmsg(intfiledes, const struct strbuf *ctlptr, const struct strbuf *dataptr, int flag);
int putpmsg(intfiledes, const struct strbuf *ctlptr, const struct strbuf *dataptr, int band,int flag);
Both return: 0if OK, 1 on error
We can also write to a stream, which is equivalent to a putmsg without any control informationand with a flag of 0.
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.
Isastream 函数就是使用ioctl的例子
#include<stropts.h> intisastream(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 validonly on a STREAMS device.
反正linux下面不管怎么测试都是个坑。。。不支持
then try to read from the first descriptor again. This type of loop is called polling. Polling should be avoided on a multitasking system.
#include <sys/select.h> int select(int maxfdp1,fd_set *restrictre adfds, fd_set *restrict writefds,fd_set *restrictexceptfds , struct timeval *restrict tvptr); Returns: count of ready descriptors, 0 on timeout, −1 on error
tvptr == NULLWait forever.This infinite wait can be interrupted if we catch a signal. Return is made when one of the specified descriptors is ready or when a signal is caught. If a signal is caught, select re turns −1 with errno set to EINTR.
tvptr !> tv_sec == 0 && tvptr !> tv_usec == 0Don’t wait at all. All the specified descriptors are tested, and return is made immediately .This is a way to poll the system to find out the status of multiple descriptors without blocking in the select function.
tvptr!>tv_sec != 0 || tvptr!>tv_usec != 0Wait the specified number of seconds and microseconds. Return is made when one of the specified descriptors is ready or when the timeout value expires. If the timeout expires before any of the descriptors is ready,t he return value is 0. (If the system doesn’t provide microsecond resolution, the tvptr!>tv_usec value is rounded up to the nearest supported value.) As with the first condition, this wait can also be interrupted by a caught signal.
#include <sys/select.h>
int FD_ISSET(int fd ,fd_set *fdset );
Returns: nonzeroif fd is in set, 0 otherwise
void FD_CLR(intfd ,fd_set *fdset );
void FD_SET(intfd ,fd_set *fdset );
void FD_ZERO(fd_set *fdset );
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(0, &readset);
FD_SET(3, &readset);
FD_SET(1, &writeset);
FD_SET(2, &writeset);
select(4, &readset, &writeset, NULL, NULL)
1. A return value of −1 means that an error occurred. This can happen, for example, if a signal is caught before any of the specified descriptors are ready. In this case, none of the descriptor sets will be modified.
2. A re turn value of 0 means that no descriptors are ready.This happens if the time limit expires before any of the descriptors are ready.When this happens, all the descriptor sets will be zeroed out.
3. A positive return value specifies the number of descriptors that are ready.This value is the sum of the descriptors ready in all three sets, so if the same descriptor is ready to be read and written, it will be counted twice in the return value. The only bits left on in the three descriptor sets are the bits corresponding to the descriptors that are ready.
#include <sys/uio.h> ssize_t readv(int fd ,const struct iovec *iov,int iovcnt ); ssize_t writev(int fd ,const struct iovec *iov,int iovcnt ); Both return: number of bytes read or written, −1 on error
struct iovec { void *iov_base; /* starting address of buffer */ size_t iov_len; /* size of buffer */ };
1. A read operation may return less than asked for,even though we have not encountered the end of file. This is not an error,and we should simply continue reading from the device.
2. A writeoperation can return less than we specified. This may be caused by kernel output buffers becoming full, for example. Again, it’s not an error,and we should continue writing the remainder of the data. (Normally,this short return from a write occurs only with a nonblocking descriptor or if a signal is
caught.)
We’ll never see this happen when reading or writing a disk file, except when the file system runs out of space or we hit our quota limit and we can’t write all that we requested.这个可以解释为什么pro_14_1.c的输出为什么没有和书上最后一个显示一样。
#include"apue.h"
ssize_treadn(int fd ,void *buf,size_tnbytes );
ssize_twriten(int fd ,void *buf,size_tnbytes );
Both return:number of bytes read or written, −1 on error
Memory-Mapped I/O
when we store data in the buffer, the corresponding bytes are automatically written to the file.This lets us perform I/O without using read or write
#include<sys/mman.h>
void *mmap(void*addr ,size_tlen,int prot ,int flag,int fd ,off_t off);
Returns:starting address of mapped region if OK,MAP_FAILED on error
The addr argument lets us specify the address where we want the mapped region to start. We normally set this value to 0 to allow thesystem to choose the starting address
prot Description
PROT_READ Region can be read.
PROT_WRITE Region can be written.
PROT_EXEC Region can be executed.
PROT_NONE Region cannot be accessed.
The protection specified for a region can’t allow more access than the open mode of the file. For example, we can’t specifyPROT_WRITE if the file was opened read-only.
If we then try to access the memory-mapped region corresponding to the end portion of the file that was truncated, we’ll receive SIGBUS.
A memory-mapped region is inherited by a child across a fork (since it’s part of the parent’s address space), but for the same reason, is not inherited by the new program across an exec.
A memory-mapped region is automatically unmapped when the process terminates or we can unmap a region directly by calling the munmap function. Closing the file descriptor used when we mapped the region does not unmap the region.
#include<sys/mman.h> int munmap(void*addr ,size_tlen); Returns: 0 ifOK,−1 on error