2017-2018-1 20155330 《信息安全系統設計基礎》第13周學習總結

2017-2018-1 20155330 《信息安全系統設計基礎》第13周學習總結

教材學習內容總結

Unix I/O

  • 基本概念:全部的I/O設備都被模型化爲文件,而全部的輸入和輸出都被看成對相應文件的讀和寫來執行。這種將設備優雅地映射爲文件的方式,容許Linux內核引出一個簡單、低級的應用接口,稱爲Unix I/O。
  • Unix I/O全部的輸入和輸出都能以一種統一且一致的方式執行:
    • 打開文件。html

      應用程序向內核發出請求→要求內核打開相應的文件→內核返回文件描述符git

    • Linux shell建立的每一個進程開始時都有三個打開的文件:標準輸入(描述符爲0)、標準輸出(描述符爲1)和標準錯誤(描述符爲2)。頭文件<unistd.h>定義常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO用來代替顯式的描述符值。
    • 改變當前的文件位置。
    • 讀寫文件。
    • 關閉文件。shell

      打開和關閉文件

      1.open函數緩存

(1)函數定義:安全

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);

(2)參數解析:網絡

flags:指明進程打算如何訪問這個文件,主要參數:數據結構

O_RDONLY:只讀
O_WRONLY:只寫
O_RDWR:可讀可寫app

O_CREAT:文件不存在,就建立新文件
O_TRUNC:若是文件存在,就截斷它
O_APPEND:寫操做前設置文件位置到結尾處
這些值能夠用或鏈接起來。
mode:指定了新文件的訪問權限位,符號名稱以下:
socket

讀和寫文件

讀和寫文件
1.讀 read函數

(1)函數原型:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t n);

(2)參數:

返回值:成功則返回讀的字節數,EOF返回0,出錯返回-1。返回值爲有符號數。

fd:文件描述符

buf:存儲器位置

n:最多從當前文件位置拷貝n個字節到存儲器位置buf
2.寫 write

(1)函數原型:

#include <unistd.h>

ssize_t write(int fd, void *buf, size_t n);

(2)參數:

返回值:成功則返回寫的字節數,出錯返回-1。返回值爲有符號數。

fd:文件描述符

buf:存儲器位置

n:最多從存儲器位置buf拷貝n個字節到當前文件位置

PS:read和write在正常狀況下返回值是實際傳送的字節數量。

3.經過lseek函數能夠顯式的修改當前文件的位置。

> ssize_t和size_t有什麼區別?

![](http://images2017.cnblogs.com/blog/1071510/201712/1071510-20171217231718421-1075649569.png)

4.不足值

在某些狀況下,read和write傳送的字節比應用程序要求的要少,緣由以下:

  • 讀的時候遇到EOF
  • 從終端讀文本行
  • 讀和寫socket

    用RIO包健壯的讀寫

    RIO,Robust I/O,自動處理不足值。

1.RIO的無緩衝的輸入輸出函數。

這些函數的做用是直接在存儲器和文件之間傳送數據,常適用於網絡和二進制數據之間。

rio_readn函數和rio_writen定義:

#include "csapp.h"

ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);

參數:

fd:文件描述符
usrbuf:存儲器位置
n:傳送的字節數
返回值:

rio_readn成功則返回傳送的字節數,EOF爲0(一個不足值),出錯爲-1

rio_writen成功則返回傳送的字節數,出錯爲-1,沒有不足值。

2.RIO的帶緩衝的輸入函數

能夠高效的從文件中讀取文本行和二進制數據。

一個文本行就是一個由換行符結尾的ASCII碼字符序列。

使用RIO函數實現一次一行的從標準輸入複製一個文本文件到標準輸出。

#include "csapp.h"

int main(int argc, char **argv) 
{
    int n;
    rio_t rio;
    char buf[MAXLINE];

    Rio_readinitb(&rio, STDIN_FILENO);//鏈接標準輸入和rio地址
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) //當成功返回時,將rio中的內容拷貝到存儲器位置buf中,最多讀maxline-1
    Rio_writen(STDOUT_FILENO, buf, n);//把存儲器位置中的數據拷貝到標註輸出中。
    exit(0);
}

讀緩衝區格式,並初始化其rio_readinitb函數。

#define RIO_BUFSIZE 8192
typedef struct {
    int rio_fd;                /* descriptor for this internal buf */
    int rio_cnt;               /* unread bytes in internal buf */
    char *rio_bufptr;          /* next unread byte in internal buf */
    char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;

void rio_readinitb(rio_t *rp, int fd) 
{
    rp->rio_fd = fd;  
    rp->rio_cnt = 0;  
    rp->rio_bufptr = rp->rio_buf;
}

RIO讀程序的核心是rio_read函數

static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
    int cnt;

    while (rp->rio_cnt <= 0) {  /* 若是緩存區爲空,調用read填滿它 */
    rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, 
               sizeof(rp->rio_buf));
    if (rp->rio_cnt < 0) {
        if (errno != EINTR) /* 出錯返回-1*/
        return -1;
    }
    else if (rp->rio_cnt == 0)  /* EOF返回0 */
        return 0;
    else 
        rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
    }

    /* 一旦緩存區非空,就從讀緩存區拷貝n和rp->rio_cnt中較小值個字節到用戶緩存區,而且返回拷貝的字節數 */
    cnt = n;          
    if (rp->rio_cnt < n)   
    cnt = rp->rio_cnt;
    memcpy(usrbuf, rp->rio_bufptr, cnt);
    rp->rio_bufptr += cnt;
    rp->rio_cnt -= cnt;
    return cnt;
}

rio_readnb函數

ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 
{
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;
    
    while (nleft > 0) {
    if ((nread = rio_read(rp, bufp, nleft)) < 0) {
        if (errno == EINTR) 
        nread = 0;      /* 調用read填充 */
        else
        return -1;      /* 錯誤,返回-1 */ 
    } 
    else if (nread == 0)
        break;              /* EOF */
    nleft -= nread;
    bufp += nread;
    }
    return (n - nleft);         /* 返回成功傳送的字節數*/
}

rio_readlineb函數

ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 
{
    int n, rc;
    char c, *bufp = usrbuf;

    for (n = 1; n < maxlen; n++) { //最可能是maxlen-1個
    if ((rc = rio_read(rp, &c, 1)) == 1) {
        *bufp++ = c;
        if (c == '\n')//找到換行符,就退出
        break;
    } else if (rc == 0) {
        if (n == 1)
        return 0; /* EOF,而且沒有讀到數據 */
        else
        break;    /* EOF,有數據,出現不足值 */
    } else
        return -1;    /* 錯誤,返回-1 */
    }
    *bufp = 0;
    return n;//返回成功傳送的字節數
}

stat須要輸入文件名,而fstat須要輸入的是文件描述符。

在stat數據結構中須要注意的有兩個變量,st_mode和st_size。

st_size:包含文件的字節數大小
st_mode:包編碼文件訪問許可位和文件類型。
許可位在Unix文件類型以下,有對應的宏指令,含義均爲「是xx嗎」,這些宏在sys/stat.h中定義:

類型 做用 函數
普通文件 二進制或文本文件(對內核沒差) S_ISREG()
目錄文件 關於其餘文件的信息 S_ISDIR()
套接字 經過網絡與其餘進程通訊的文件 S_ISSOCK()
  • 查詢和處理一個文件的st_mode位:
#include "csapp.h"

int main (int argc, char **argv) 
{
    struct stat stat;
    char *type, *readok;

    Stat(argv[1], &stat);//文件選擇argv[1],寫入一個stat數據結構
    if (S_ISREG(stat.st_mode))     /* 若是是一個文本文件 */
    type = "regular";
    else if (S_ISDIR(stat.st_mode))//若是是一個目錄文件
    type = "directory";
    else 
    type = "other";
    if ((stat.st_mode & S_IRUSR)) /* 檢查閱讀權限 */
    readok = "yes";
    else
    readok = "no";

    printf("type: %s, read: %s\n", type, readok);
    exit(0);
}

I/O重定向

I/O重定向操做符命令:ls > foo.txt
使外殼加載和執行ls程序,而且將標準輸出重定向到磁盤文件foo.txt。

I/O重定向函數: dup2

函數定義爲:

#include <unistd.h>

int dup2(int oldfd, int newfd);
返回值:成功返回描述符,錯誤返回-1

這個函數執行的操做是,拷貝描述符表表項oldfd,覆蓋描述表表項newfd,若是後者被打開,則在拷貝前關閉它。

標準I/O

  1. 標準I/O庫:

ANSI C定義了一組高級輸入輸出函數,稱爲標準I/O庫,包含:

fopen、fclose,打開和關閉文件

fread、fwrite,讀和寫字節

fgets、fputs,讀和寫字符串

scanf、printf,複雜的格式化的I/O函數

  1. 流——類型爲FILE的流是對文件描述符和流緩衝區的抽象

標準I/O庫將一個打開的文件模型化爲一個流。

每一個ANSI C程序開始的時候都有三個打開的流:stdin、stdout、stderr,對應於標準輸入、標準輸出和標準錯誤,定義以下:

#include <stdio.h>

extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

套接字

網絡套接字上最好不要使用標準I/O函數,而是使用RIO函數,緣由:

若是沒有清楚緩存區,輸入函數後面不能接輸出函數,輸出函數後面也不能接輸入函數,而對套接字使用lseek是非法的,打開兩個流有很麻煩,因此!在網絡套接字上不要使用標準I/O函數來進行輸入和輸出!

  • 錯誤處理
    附錄A中主要講了這本書中的錯誤處理方式,有一個方法——錯誤處理包裝函數,這個思想頗有意思,至關於給基本函數再套上一層皮,而後run這個皮,發現了錯誤就終止,徹底正確的話就跟沒有這層皮同樣。
  1. 錯誤處理風格

(1)Unix風格

遇到錯誤後返回-1,而且將全局變量errno設置爲指明錯誤緣由的錯誤代碼;

若是成功完成,就返回有用的結果。

(2)Posix風格

返回0表示成功,返回非0表示失敗;

有用的結果在傳進來的函數參數中。

(3)DNS風格

有兩個函數,gethostbyname和gethostbyaddr,失敗時返回NULL指針,並設置全局變量h_errno。

(4)錯誤報告函數

void unix_error(char *msg) /* unix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(errno));
    exit(0);
}
/* $end unixerror */

void posix_error(int code, char *msg) /* posix-style error */
{
    fprintf(stderr, "%s: %s\n", msg, strerror(code));
    exit(0);
}

void dns_error(char *msg) /* dns-style error */
{
    fprintf(stderr, "%s: DNS error %d\n", msg, h_errno);
    exit(0);
}

void app_error(char *msg) /* application error */
{
    fprintf(stderr, "%s\n", msg);
    exit(0);
}

2.錯誤處理包裝函數

  • Unix風格

成功時返回void,返回錯誤時包裝函數打印一條信息,而後退出。

void Kill(pid_t pid, int signum) 
{
    int rc;

    if ((rc = kill(pid, signum)) < 0)
    unix_error("Kill error");
}
  • Posix風格

成功時返回void,錯誤返回碼中不會包含有用的結果。

void Pthread_detach(pthread_t tid) {
    int rc;

    if ((rc = pthread_detach(tid)) != 0)
    posix_error(rc, "Pthread_detach error");
}
  • DNS風格
struct hostent *Gethostbyname(const char *name) 
{
    struct hostent *p;

    if ((p = gethostbyname(name)) == NULL)
    dns_error("Gethostbyname error");
    return p;
}

本章習題

    • 運行結果:
    • 運行結果:
    • 運行結果:
  1. 調用dup2(5,0)/dup2(5,STDIN_FILENO)

    • 運行結果:

代碼託管

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 10/10
第二週 63/63 1/2 8/18
第三週 31/94 1/3 18/36
第四周 265/329 1/4 17/53
第五週 106/435 2/6 18/71
第六週 211/646 2/8 21/92
第七週 1420/2066 2/10 17/109
第八週 1061/3127 1/11 17/126
第九周 1458/4585 3/14 20/146
第十週 1410/5995 1/15 20/166
第十一週 779/6774 2/17 18/184
第十三週 326/7100 2/19 20/204

嘗試一下記錄「計劃學習時間」和「實際學習時間」,到期末看看能不能改進本身的計劃能力。這個工做學習中很重要,也頗有用。
耗時估計的公式
:Y=X+X/N ,Y=X-X/N,訓練次數多了,X、Y就接近了。

參考:軟件工程軟件的估計爲何這麼難軟件工程 估計方法

  • 計劃學習時間:20小時

  • 實際學習時間:20小時

參考資料

相關文章
相關標籤/搜索