APUE 學習筆記(九) 高級I/O

1. 非阻塞I/O

低速系統調用時 可能會使進程永遠阻塞的一類系統調用,包括如下調用:
(1)某些文件類型你( 網絡socket套接字、終端設備、管道)暫無可以使用數據,則讀操做可能會使調用者永遠阻塞
(2)若是數據不能當即被(1)中文件類型接受,則寫操做會使調用者永遠阻塞
(3)某些進程間通訊函數
 
非阻塞I/O使咱們能夠調用open、read、write這樣的I/O操做,並使這些操做不會永遠阻塞,若是這種操做不能完成,則調用當即出錯返回
 
對於一個給定的文件有兩種方法對其指定非阻塞I/O:
(1)調用open打開文件時,指定 O_NONBLOCK標誌
(2)對於一個打開的描述符,能夠 調用fcntl函數,將文件設置爲非阻塞I/O
 

2. fcntl記錄鎖

當一個進程正在讀或修改文件的某個部分時,它能夠組織其它進程修改同一文件區,它鎖定的只是文件的一個區域(也多是整個文件)
struct flock {
    short     l_type;      /* F_RDLCK, F_WRLCK, F_UNLCK */
    off_t      l_start;      /* offset in bytes, relative to l_whence */
    short     l_whence; /*  SEEK_SET, SEEK_CUR, SEEK_END */
    off_t      l_len;        /*  length in bytes, 0 means lock to EOF */
    pid_t     l_pid;        /*  returned with F_GETLK */
};
#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;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len = len;
    int ret = fcntl(fd, cmd, &lock);
    return ret;
}

若是兩個進程相互等待對方持有而且鎖定的資源時,則這兩個進程處於死鎖狀態網絡

3. select

#include <select.h>
int select(int maxfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

中間三個參數 readfds, writefds, exceptfds是指向描述符集的指針,每一個描述符集存放在一個fd_set中:app

   

這三個參數指針任意一個能夠爲空指針,表示對相應狀態並不關心。若是三個指針都是空指針,則select提供了比sleep更精確的計時器,由於sleep只能等待整數秒,socket

而select的struct timeval能夠精確到微秒函數

int  FD_ISSET(int fd, fd_set* set);
void FD_CLR(int fd, fd_set* set);
void FD_SET(int fd, fd_set* set);
void FD_ZERO(fd_set* set);

select的第一個參數maxfds是三個描述符集中最大的fd數值加1,也能夠將此參數設置爲FD_SETSIZE,代表最大的描述符數spa

fd_set readset, writeset;
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);

  

由於描述符編號從0開始,因此要在最大描述符編號值加1,第一個參數實際上就是要檢查的描述符數(從描述符0開始)指針

select有3個可能的返回值:
(1)返回值-1表示出錯
(2)返回值0表示沒有描述符準備好或者超時
(3)返回值爲正數表示已經準備好的描述符數,該值是三個描述符集中已準備好的描述符之和,若是同一描述符已經準備好讀和寫,那麼返回值中計爲2
 

4. readn和writen

int readn(int fd, char* ptr, size_t n)
{
    size_t  nleft = n;
    int     nread = 0;
    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (nleft == n) {
                return -1;
            } else {
                break;
            }
        } else if (nread == 0) {
            break;
        }
        nleft -= nread;
        ptr   += nread;
    }
    return n - nleft;
}
int writen(int fd, char* ptr, size_t n)
{
    size_t  nleft = n;
    int     nwrite = 0;
    while (nleft > 0) {
        if ((nwrite = write(fd, ptr, nleft)) < 0) {
            if (nleft == n) {
                return -1;
            } else {
                break;
            }
        } else if (nwrite == 0) {
            break;
        }
        nleft -= nwrite;
        ptr   += nwrite;
    }
    return n - nleft;
}
 

5. 存儲映射I/O mmap

Memory-mapped I/O 使一個外存磁盤文件與內存空間中的一個緩衝區相映射,當從緩衝區中取數據,就至關於讀磁盤文件中的相應字節,寫狀況亦如此
#include <sys/mman.h>
void* mmap(void* addr, size_t len, int prot, int flag, int fd, off_t off);
addr參數指定映射存儲區的起始位置,一般設置爲0,表示由內核選擇該映射區的起始地址
fd指定被映射文件的描述符,len是映射的字節數,off是映射字節在文件中的起始偏移量
 
addr和off的值一般應當是系統虛擬頁長度的倍數,由於 addr和off經常指定爲0,因此這要求通常不重要
由於映射文件的起始偏移量受系統虛擬頁大小限制。假定文件長12字節,系統頁長512字節,則系統一般提供512字節的映射區, 其中後500字節被設置爲0,對該500字節的任何修改都不會在文件中反映出來。
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    int fdin = 0;
    int fdout = 0;
    char* src = NULL;
    char* dst = NULL;
    struct stat statbuf;
    if (argc != 3) {
        fprintf(stderr, "usage: %s <fromfile> <tofile>\n", argv[0]);
        return 1;
    }
    if ((fdin = open(argv[1], O_RDONLY)) < 0) {
        fprintf(stderr, "cannot open %s for reading\n", argv[1]);
    }
    if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC)) < 0) {
        fprintf(stderr, "cannot creat %s for writing\n", argv[2]);
    }
    if (fstat(fdin, &statbuf)) {
        fprintf(stderr, "fsat error\n");
    }

    if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) {
        fprintf(stderr, "lseek error\n");
    }
    if (write(fdout, "", 1) != 1) {
        fprintf(stderr, "write error\n");
    }

    if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED) {
        fprintf(stderr, "mmap error for input\n");
    }

    if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == MAP_FAILED) {
        fprintf(stderr, "mmap error for output\n");
    }
    memcpy(dst, src, statbuf.st_size);
    munmap(src, statbuf.st_size);
    munmap(dst, statbuf.st_size);
    return 0;
}

 

 
相關文章
相關標籤/搜索