Linux應用程序開發

整理:Jims of 肥肥世家

<[email protected]>

Copyright ? 2006 本文遵從GNU 的自由文檔許可證(Free Documentation License)的條款,歡迎轉載、修改、散佈。

發佈時間:2006年11月01日

更新時間:2007年04月05日,增加數據結構基礎。

Abstract

我的Linux應用程序開發筆記,從這裏開始我的Linux開發之旅。

Table of Contents

1. C語言基礎

    1.1. 數據類型
    1.2. 關鍵字
    1.3. 變量等級
    1.4. 特殊字符的表示方法:
    1.5. 指針與數組
    1.6. ANSI標準頭文件

2. 使用GCC編譯程序
3. 使用gdb調試程序
4. Linux程序開發基礎

    4.1. 路徑
    4.2. 庫文件
    4.3. 預處理
    4.4. 系統調用(system call)

5. 文件處理
6. Linux環境編程

    6.1. 參數選項
    6.2. 環境變量
    6.3. 時間
    6.4. 臨時文件
    6.5. 用戶信息
    6.6. 日誌信息

7. 進程

    7.1. 進程狀態

8. 串口編程

    8.1. 常用函數
    8.2. 設置串口屬性
    8.3. c_iflag輸入標誌說明
    8.4. c_oflag輸出標誌說明
    8.5. c_cflag控制模式標誌說明
    8.6. c_cc[]控制字符說明
    8.7. c_lflag本地模式標誌說明
    8.8. 下面介紹一些常用串口屬性的設置方法。

9. 安全

    9.1. 內核漏洞介紹

10. 數據結構(Data Structure)

    10.1. 基礎概念

List of Tables

1.1. 特殊字符的表示方法

Chapter 1. C語言基礎

Table of Contents

1.1. 數據類型
1.2. 關鍵字
1.3. 變量等級
1.4. 特殊字符的表示方法:
1.5. 指針與數組
1.6. ANSI標準頭文件

Linux是使用C語言開發的,基於Linux平臺的應用程序開發,C語言是首選的開發語言。本章記錄C語言的基本概念和基礎知識。
1.1. 數據類型

整數類型(int),

各種整數數製表示法:

    *

      ddd,十進制表示法,d爲0--9的整數,但不能以0開頭。如:123,345。
    *

      0ooo,八進制表示法,以0(數字0)開頭,o爲0--7的整數。如:010(八進制)=8(十進制),014(八進制)=12(十進制)。
    *

      0xhhh,十六進制表示法,以0x或0X開頭,h爲0--9、A、B、C、D、E、F。如:0x10(十六進制)=16(十進制),0xA(十六進制)=10(十進制)。
    *

      以L或l結尾的數表示長整數(long int),編譯器會以32位空間存放此數字,但GCC默認是以32位存放整數,所以此表示法在Linux下沒什麼作用。

1.2. 關鍵字

關鍵字是C語言本身保留使用的,不能用於變量和函數名。

auto          double          int          struct
break         else            long         switch
case          enum            register     typedef
char          extern          return       union
const         float           short        unsigned
continue      for             signed       void
default       goto            sizeof       volatile
do            if              static       while

1.3. 變量等級

    *

      auto,內部變量,在函數內部聲明。只能在函數內部使用,它的生命週期從調用函數開始,到函數執行完時消失。內部變量以堆棧存放,必須在函數執行時纔會存在,這種方式稱爲聲明。auto可省略。如:

auto int i = 0;
/* 可寫成int i = 0; */

      內部變量的優缺點:
          o

            內部變量只在函數內有效,能提高函數的安全。
          o

            內部變量在函數結束時消失,不會長期佔用內存空間,能提高內存的利用率。
          o

            內部變量的缺點是生命週期短,函數運行結束後不能保留。
    *

      static auto,內部靜態變量,在函數內部定義,auto也可省略。內部靜態變量以固定地址存放,編譯時就已分配置內在空間,這種方式稱爲定義。由於有固定地址,函靜態變量不會隨函數的結束而消失。static變量會一直保存在內存空間中,當函數再次執行時,上次保留的使用靜態變量可以繼續使用。如:

static int i = 0;

    *

      extern,外部變量,是在函數外定義的變量,可被多個函數存取。在外部變量定義覆蓋範圍之內的函數內可以自由使用外部變量。不在外部變量定義覆蓋範圍之內的函數要使用外部變量就要先使用extern關健字來聲明外部變量。

int i;      /* 外部變量定義,在main函數外 */

int main(void)
{
i = 1;            /* main()函數位於外部變量i定義的下面,不用聲明可直接使用 */
printf("%d/n", i);
}

      不在外部變量定義覆蓋範圍之內的函數要使用外部變量就要先使用extern關健字來聲明外部變量。

int main(void)
{
extern int i;        /* 外部變量i在main()函數之後定義,需用extern關鍵字聲明後才能使用 */

i = 1;
printf("%d/n",i);
}
int i;
...

      在另外的程序文件中我們也可以通過擴展聲明使用其它程序文件中的外部變量。

程序1 hello.c
#include <stdio.h>

int main(void)
{
        extern int i;  //擴展聲明外部變量
        i = 333;
        printf("%d/n", i);

        extern des(void);  //擴展聲明外部函數
        des();
}
int i;         //外部變量定義

程序2 hello1.c
#include <stdio.h>

extern int i;      //擴展聲明其它程序文件中的外部變量

void des()
{
        i++;
        printf("%d/n",i);
}                                              

      編譯

debian:~/c# gcc hello.c hello1.c
debian:~/c# ./a.out
333
334

      外部變量有效範圍總結:
          o

            由外部變量定義的位置開始,至文件結尾。
          o

            不在有效範圍內的函數,也可通過extern擴展聲明使用定義的外部變量,且可在多個函數中使用。注// 意:在各函數中使用的外部變量是一樣的,對該變量的修改會影響到其它函數內的同一變量。
          o

            可用extern擴展聲明使用另外一個程序文件中的外部變量。

      外部變量的優點是生命週期長,可在函數間共享數據和傳輸數據。缺點是變量安全性較低,但可通過合理設置外部變量的有效範圍提高安全性。
    *

      static extern,外部靜態變量,在函數外部定義,只供單一程序文件使用,即使其它程序文件定義了同樣名稱的變量,編譯器也把它當成另外一個變量處理。外部靜態變量能有效隔離變量在一個程序文件中。

static int i;

    *

      register,register變量是以寄存器(register)來存放變量,而不是一般內存。只有內部變量才能使用register類型變量。使用這種變量能加快變量的處理速度。但缺點是要佔用CPU寄存器。如:

register int i;
register int j;

變量等級的概念也同樣適用於函數。若想調用不在有效範圍內的函數,則要用extern擴展聲明函數的有效範圍。

內部變量是以堆棧方式存放的,必須在函數執行時纔會存在,所以稱爲聲明(Declaration)。其它如static auto、extern和static extern等級的變量,都是以固定的地址來存放的,而不是以堆棧方式存放的,在程序編譯時就已分配了空間,所以稱之爲定義(Definition)。
1.4. 特殊字符的表示方法:

Table 1.1. 特殊字符的表示方法
符號 ASCII字符(十六進制) 句柄符號 作用
/a 07 BEL 響鈴
/b 08 BS 回格
/f 0C FF 換頁
/n 0A LF 換行
/r 0D CR 回車鍵
/t 09 HT [tab]鍵
/v 0B VT 空行
/0 00 NUL 空字符
// 5C / 反斜槓
/' 2C ' 單引號
/" 22 " 雙引號
/? 3F ? 問號
1.5. 指針與數組

    *

      C語言中專門用來存放內存地址的變量叫指針(pointer)變量,簡稱指針。
    *

      &運算符用來取得變量地址,
    *

      "*"運算符用來取得指針變量的值。
    *

      數組名就是地址變量,指向內存中存放第一個數組元素的地址。數組元素編號從0開始,如a[0]表示數組a的第一個元素。

數組是內存中的連續區間,可根據聲明類型存放多種數值類型。如:

int a[10];         聲明一個有10個int元素的數組
char b[20];        聲明一個有20個char元素的數組

指針示例:

int *p;            /* p是一個指針,p的內容是內存的地址,在這個地址中將存放一個整數。

數組名和指針都是用來存放內存地址的,不過數組名具有固定長度,不可變。而指針與一般變量一樣,其值是可變的。
1.6. ANSI標準頭文件

Linux系統頭文件位於/usr/include中。默認情況下編譯器只在該目錄下搜索頭文件。

    *

      assert.h
    *

      ctype.h
    *

      errno.h
    *

      float.h
    *

      limits.h
    *

      locale.h
    *

      math.h
    *

      setjmp.h
    *

      signal.h
    *

      stdarg.h
    *

      stddef.h
    *

      stdio.h
    *

      stdlib.h
    *

      string.h
    *

      time.h

Chapter 2. 使用GCC編譯程序

直接生成a.out可執行文件

debian:~/c# gcc hello.c

編譯hello.c程序,生成hello可執行文件:

debian:~/c# gcc -o hello hello.c

生成.s的彙編代碼文件。

debian:~/c# gcc -S hello.c

Chapter 3. 使用gdb調試程序

如果想利用gdb工具來調試程序,在編譯程序時要使用-g選項。如:

debian:~/c# gcc -g serial.c -o serial

調試serial程序。

debian:~/c# gdb serial
GNU gdb 6.5-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) list
8       #include     <errno.h>      /*錯誤號定義*/
9
10      int main(void)
11      {
12              int fd,n,status,buffsize;
13              struct termios a;
14              struct termios *oldtio;
15              char m[255],*comm;
16
17              fd = open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
(gdb)

gdb的list命令是列出程序源碼。下面介紹gdb下的各種操作。

    *

      list,列出程序源代碼,一次只列出10行的內容。list命令可以指定範圍。如:list 5,10可列出第5行到第10行的內容。
    *

      run,執行程序。按Ctrl+c可中斷程序的執行。
    *

      shell,暫時退出gdb回到shell環境。在shell環境用exit命令可以返回gdb。
    *

      break,設置斷點,後跟行號則把斷點設置在指定的行號,後跟函數名則把斷點設置在函數。如break 6,break function。還可根據條件設置斷點,如:break 9 if result > 50。這條命令的意思是,當運行到第9行時,如果result變量的值大於50,則中斷程序。

(gdb) break 6
Breakpoint 1 at 0x8048634: file serial.c, line 6.

    *

      watch,指定條件,如果成立則中斷。如:watch result >50。當result的變量大於50時,馬上中斷程序。
    *

      print,打印變量值,如:print result。
    *

      whatis,查看變量類型,如:whatis result。
    *

      continue,從中斷點繼續運行程序。
    *

      step,從中斷點開始單步運行,如果遇到函數,則進入函數單步運行。
    *

      next,從中斷點開始單步運行,如果遇到函數,則運行函數,該命令不會進入函數單步運行,而是運行整個函數。
    *

      info breakpoints,查看程序中所設置的所有中斷點信息。

(gdb) info breakpoints
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x08048634 in main at serial.c:6

      Enb字段是"y",表示斷點1現正生效。
    *

      disable/enable,控制中斷點失效和啓用。如:disable 1。如果disable/enable命令後沒有指定斷點號,則該命令作用於所有已設置的斷點。

(gdb) disable 1
(gdb) info breakpoints
Num Type           Disp Enb Address    What
1   breakpoint     keep n   0x08048634 in main at serial.c:6

      Enb字段由"y"變成"n",斷點1暫時被禁止。
    *

      enable once,使斷點生效一次。
    *

      delete,刪除斷點。如:delete 1。delete要指定斷點號。
    *

      clear,刪除斷點。如:clear 6。clear要指定設置斷點的行號或函數名。
    *

      help all,顯示所有gdb環境的命令。

[Note] 
在gdb環境下,按tab鍵可自動補全命令。直接按回車鍵可重複執行上一個操作。按上下光標鍵可顯示歷史命令。
Chapter 4. Linux程序開發基礎

Table of Contents

4.1. 路徑
4.2. 庫文件
4.3. 預處理
4.4. 系統調用(system call)

4.1. 路徑

在設置Linux的系統路徑時,使用冒號分隔每個路徑名。如:

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"

在Linux中的程序有兩種,一種是可執行程序,與Windows下的.exe文件類似,一種是腳本,與Windows下的.bat文件類似。

Linux中常用的程序存放路徑有以下幾個:

    *

      /bin,該路徑存放系統啓動時需要使用的程序。
    *

      /usr/bin,該路徑存放用戶需使用的標準程序。
    *

      /usr/local/bin,該路徑存放本地安裝的程序。
    *

      Linux使用斜槓"/"分隔路徑名,而不是Windows的反斜槓"/"。
    *

      Linux下的C編譯器使用GCC,由於歷史的原因,在POSIX兼容的操作系統中,C編譯器都叫cc,所以Linux下也有一個cc命令,它是一個到gcc的軟鏈接。

開發工具,多數位於/usr/bin或/usr/local/bin目錄下。

頭文件,位於/usr/include目錄。頭文件包含有常量定義、系統調用和庫函數調用的聲明。這是系統默認的頭文件存放路徑,在編譯程序時,編譯器會自動查找該目錄。gcc編譯器在編譯程序時也可用-I參數指定另外的頭文件路徑。如:

gcc -I/usr/local/myinclude test.c。

4.2. 庫文件

庫文件,庫是一組已編譯的函數集合,可方便我們重用代碼。默認存放在/lib和/usr/lib目錄。庫文件可分爲靜態和共享兩類。

    *

      .a,靜態庫文件。使用靜態庫將會把所有的庫代碼引入程序,佔用更多的磁盤空間和內存空間,所以一般建議使用共享庫。
    *

      .so,共享庫文件。使用共享庫的程序不包含庫代碼,只在程序運行才調用共享庫中的代碼。

在編譯時可用包含路徑的庫文件名或用-l參數指定使用的庫文件,/usr/lib/libm.a等價於-lm。如:

gcc -o hello hello.c /usr/lib/libm.a 
或用-l參數寫成
gcc -o hello hello.c -lm

如果我們要使用的庫文件不在默認位置,在編譯程序時可用-L參數指定庫文件的路徑。下面例子使用了/usr/hello/lib目錄下的libhello庫文件:

gcc -o hello -L/usr/hello/lib hello.c -lhello

創建和使用靜態庫。

    *

      分別創建兩個函數,函數a的內容如下:

#include <stdio.h>

void a(char *arg)
{
        printf("function a,hello world %s/n",arg);
}

      函數b的內容如下:

#include <stdio.h>

void b(int arg)
{
        printf("function b,hello world %d/n",arg);
}

    *

      接着,生成兩個對象文件。

debian:~/c# gcc -c a.c b.c
debian:~/c# ls *.o
a.o  b.o

    *

      最後,用ar歸檔命令把生成的對象文件打包成一個靜態庫libhello.a。

debian:~/c# ar crv libhello.a a.o b.o
r - a.o
r - b.o

    *

      爲我們的靜態庫定義一個頭文件lib.h,包含這兩個函數的定義。

/*
 * this is a header file.
 */
void a(char *arg);
void b(int arg);
}}}
  * 創建jims.c程序,內容如下。{{{#!cplusplus
#include "lib.h"

int main()
{
        a("jims.yang");
        b(3);
        exit(0);
}

    *

      利用靜態鏈接庫編譯程序。

debian:~/c# gcc -c jims.c
debian:~/c# gcc -o jims jims.o libhello.a
debian:~/c# ./jims
function a,hello world jims.yang
function b,hello world 3
debian:~/c#                                      

      [Note] 
      gcc -o jims jims.o libhello.a也可以寫成gcc -o jims jims.o -L. -lhello。

共享庫比靜態庫具有以下的優點:

    *

      當多個進程使用同一共享庫時,Linux會把共享庫中存放可執行代碼的內存進行共享。所以共享庫可節省內存,提高系統性能。
    *

      程序可共享代碼,減少磁盤空間佔用。
    *

      共享庫出錯,只要重新編譯共享庫即可,不用重新編譯應用程序。

ldconfig程序用來安裝一個共享庫,。

只有在爲系統庫安裝一個庫的時候,才需要在/etc/ld.so.conf中創建記錄,並運行ldconfig更新共享庫的緩存。

LD_LIBRARY_PATH環境變量用來指定附加的庫文件路徑。系統默認的庫文件路徑位於/usr/lib和/lib目錄下。

LD_PRELOAD環境變量指定提前載入的庫,用於替代系統庫。
4.3. 預處理

預處理,在程序開頭以“#”開頭的命令就是預處理命令,它在語法掃描和分析法時被預處理程序處理。預處理有以下幾類:

    *

      宏定義,用#define指令定義。如:#define BUFFER 1024。取消宏定義用#undef指令。宏還可帶參數,如:

#define BUF(x) x*3

    *

      包含頭文件,用#include指令,可把包含的文件代碼插入當前位置。如:

<#include <stdio.h>。

      包含的文件可以用尖括號,也可用雙引號,如:

#include "stdio.h"。

      不同之處是,使用尖括號表示在系統的包含目錄(/usr/include)下查找該文件,而雙引號表示在當前目錄下查找包含文件。每行只能包含一個包含文件,要包含多個文件要用多個#include指令。
    *

      條件編譯,格式如下:

格式一,如果定義了標識符,則編譯程序段1,否則編譯程序段2:
#ifdef 標識符
程序段1
#else
程序段2
#endif

格式二,如果定義了標識符,則編譯程序段2,否則編譯程序段1,與格式一相反:
#ifndef 標識符
程序段1
#else
程序段2
#endif

格式三,常量表達式爲真則編譯程序段1,否則編譯程序段2:
#if 常量表達式
程序段1
#else
程序段2
#endif

使用gcc編譯程序時,要經過四個步驟。

    *

      預處理(Pre-Processing),用-E參數可以生成預處理後的文件。

debian:~/c# gcc -E hello.c -o hello.i

    *

      編譯(Compiling)
    *

      彙編(Assembling)
    *

      鏈接(Linking)

GCC默認將.i文件看成是預處理後的C語言源代碼,所以我們可以這樣把.i文件編譯成目標文件。

debian:~# gcc -c hello.i -o hello.o}}}

在GCC中使用-pedantic選項能夠幫助程序員發現一些不符合ANSI/ISO C標準的代碼,但不是全部。從程序員的角度看,函數庫實際上就是一些頭文件(.h)和庫文件(.so或者.a)的集合。
4.4. 系統調用(system call)

要理解系統調用就要先理解程序代碼運行的兩種模式,一種是用戶模式,一種是內核模式。我們編寫的應用程序運行在用戶模式下,而設備驅動程序和文件系統運行在內核模式。在用戶模式下運行的程序受到嚴格的管理,不會破壞系統級應用。而在內核模式下運行的程序可以對電腦有完全的訪問權。系統調用就是運行在內核模式下的代碼爲運行在用戶模式下的代碼提供服務。

系統調用的錯誤返回碼是負數,定義在>errno.h<文件中。在系統調用中發生錯誤,C函數庫就會用錯誤碼填充全局變量errno。用perror()和strerror()函數可以輸出錯誤信息。

系統調用多數在>unistd.h<中定義。
Chapter 5. 文件處理

在Linux系統內所有東西都是以文件的形式來表示的,除一般的磁盤文件外,還有設備文件,如硬盤、聲卡、串口、打印機等。設備文件又可分爲字符設備文件(character devices)和塊設備文件(block devices)。使用man hier命令可以查看Linux文件系統的分層結構。文件的處理方法一般有五種,分別是:

    *

      open,打開一個文件或設備。
    *

      close,關閉一個打開的文件或設備。
    *

      read,從一個打開的文件或者設備中讀取信息。
    *

      write,寫入一個文件或設備。
    *

      ioctl,把控制信息傳遞給設備驅動程序。

open, close,read,write和ioctl都是低級的,沒有緩衝的文件操作函數,在實際程序開發中較少使用,一般我們使用標準I/O函數庫來處理文件操作。如:fopen,fclose,fread,fwrite,fflush等。在使用標準I/O庫時,需用到stdio.h頭文件。

    *

      fopen()這個標準I/O庫函數用於打開文件,在Linux中文件要先打開後才能進行讀寫操作。

#include <stdio.h>
FILE *fopen(const char *filename, const char *mode);

*mode選項:
"r" 或o"rb"  爲讀打開文件
"w" 或 "wb" b爲寫打開文件,如果文件不存在則創建,如果存在則覆蓋
"a" 或 "ab" b爲追加內容而打開文件
"r+" 或o"rb+" 或 "r+b"r 爲更新打開文件,不會覆蓋舊文件
"w+" 或 "wb+"b或 "w+b"w 爲更新打開文件,會覆蓋舊文件
"a+"a或 "ab+" 或 "a+b"  爲更新打開文件,更新內容追加到文件末尾

一些常用的文件和目錄維護函數:chmod、chown、unlink、link、symlink、mkdir、rmdir、chdir、getcwd、opendir,closedir、readdir、telldir、seekdir等。

fcntl用於維護文件描述符,mmap用於分享內存。

創建文檔並輸入信息的示例代碼:

#include <stdio.h>

main(void)
        {
        FILE *fp1;
        char c;

        fp1 = fopen("text.txt","w");
        while ((c = getchar())!= '/n')
                putc(c,fp1);
        fclose(fp1);
        }

顯示路徑的示例代碼

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
        char *topdir = ".";
        if (argc >= 2)
                topdir = argv[1];

        printf("Directory scan of %s/n", topdir);
        printdir(topdir,0);
        printf("done./n");

        exit(0);
}

printdir(char *dir, int depth)
{
        DIR *dp;
        struct dirent *entry;
        struct stat statbuf;

        if((dp = opendir(dir)) == NULL)
        {
                fprintf(stderr,"cannot open directory:%s/n",dir);
                return;
        }
        chdir(dir);
        while((entry = readdir(dp)) != NULL)
        {
                lstat(entry->d_name,&statbuf);
                if(S_ISDIR(statbuf.st_mode))
                {
                        if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
                                continue;
                        printf("%*s%s//n",depth,"",entry->d_name);
                        printdir(entry->d_name,depth+4);
                }
                else printf("%*s%s/n",depth,"",entry->d_name);
        }
        chdir("..");
        closedir(dp);
}

Chapter 6. Linux環境編程

Table of Contents

6.1. 參數選項
6.2. 環境變量
6.3. 時間
6.4. 臨時文件
6.5. 用戶信息
6.6. 日誌信息

6.1. 參數選項

void main()表示程序沒有參數,int main(int argc, char *argv[])表示程序要帶參數,argc保存着參數的個數,argv[]數組保存着參數列表。如:

debian:~# mytest a b c
argc: 4
argv: ["mytest","a","b","c"]

getopt()函數和getopt_long()用來處理程序選項。getopt_long()函數可以處理以"--"開頭的選項。Gnu官方手冊頁:http://www.gnu.org/software/libc/manual/html_node/Getopt.html

獲取命令行參數的示例代碼:

#include <stdio.h>
#include <unistd.h>

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

   while((opt = getopt(argc,argv,"if:lr")) != -1)       /* 返回“-1”表示已沒選項需要處理。*/
   {
           switch(opt){
           case 'i':
           case 'l':
           case 'r':
                   printf("option: %c/n", opt);
                   break;
           case 'f':
                   printf("filename: %s/n", optarg);           /*如果選項需要一個參數,則參數存放在外部變量optarg中。*/
                   break;
           case ':':
                   printf("option needs a value /n");          /*“:”表示選項需要參數*/
                   break;
           case '?':
                   printf("unknown option: %c/n", optopt);    /*返回“?”表示無效的選項,並把無效的選項存放在外部變量optopt中。*/
                   break;
           }
   }
   for(; optind < argc; optind++)                      /*外部變量optind指向下一個要處理的選項索引值。*/
           printf("argument: %s/n", argv[optind]);
}

6.2. 環境變量

在bash shell中使用set命令可以列出Linux系統的環境變量,在C程序中我們也可以用putenv()和getenv()函數來獲取Linux系統的環境變量。這兩個函數的聲明如下:

char *getenv(const char *name);
int putenv(const char *string);

系統有一個environ變量記錄了所有的系統變量。下面的示例代碼可把environ的值顯示同來。

#include <stdlib.h>
#include <stdio.h>

extern char **environ;

int main()
{
        char **env = environ;

        while(*env)
        {
                printf("%s/n",*env);
                env++;
        }
}

6.3. 時間

linux和其它unix一樣,使用GMT1970年1月1日子夜作爲系統時間的開始,也叫UNIX紀元的開始。現在的時間表示爲UNIX紀元至今經過的秒數。

#include <time.h>
 time_t time(time_t *t);

顯示系統時間的示例代碼:
#include <time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
        int i;
        time_t the_time;

        for(i = 1; i <= 10; i++){
                the_time = time((time_t *)0);
                printf("%d  the time is %ld/n", i, the_time);
                sleep(2);
        }
}

用ctime()函數以友好方式返回當前時間,它的函數聲明格式:

#include <time.h>
char *ctime(const time_t *timeval);

示例:
#include <time.h>
#include <stdio.h>

int main(void)
{
        time_t time1;

        (void)time(&time1);
        printf("The date is: %s/n",ctime(&time1));
}

程序輸出:
The date is: Thu Dec  7 09:58:23 2006

用localtime()函數可以返回本地時間,它是一個tm結構,tm結構體的內容如下:

 struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};

int tm_sec    Seconds, 0-61
int tm_min    Minutes, 0-59
int tm_hour   Hours, 0-23
int tm_mday   Day in the month, 1-31
int tm_mon    Month in the year, 0-11(January= 0)
int tm_year   Years since 1900
int tm_wday   Day in the week, 0-6. (Sunday = 0)
int tm_yday   Day in the year, 0-365
int tm_isdst  Daylight savings in effect

localtime()函數的使用方法如下:

函數聲明:
#include <time.h>
struct tm *localtime(const time_t *timeval);

示例代碼:
#include <time.h>
#include <stdio.h>

int main(void)
{
        time_t time1;
        struct tm *p;

        time1 = time(NULL);
        printf("The ctime is: %s/n",ctime(&time1));

        p = localtime(&time1);
        printf("The localtime is:/n tm_year+1900 = %d年/n tm_mon = %d月/n tm_mday = %d日/n wday = %d/n hour = %d 時/n min = %d分/n sec = %d秒/n",p->tm_year+1900,p->tm_mon,p->tm_mday,p->tm_wday,p->tm_hour,p->tm_min,p->tm_sec);
}
運行結果:
The ctime is: Thu Dec  7 10:31:36 2006

The localtime is:
 tm_year+1900 = 2006年
 tm_mon = 11月
 tm_mday = 7日
 wday = 4
 hour = 10 時
 min = 31分
 sec = 36秒

6.4. 臨時文件

用mkstemp()函數創建臨時文件。

#include<stdlib.h>
int mkstemp(char * template);

示例:
#include <stdio.h>

int main(void)
{
        char template[] = "template-XXXXXX";
        int fp;
        fp = mkstemp(template);
        printf("template = %s/n", template);
        close(fp);
}

6.5. 用戶信息

獲取用戶信息。

聲明:
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);   /* 根據uid返回用戶信息 */
struct passwd *getpwnam(const char *name);   /* 根據用戶名返回用戶信息 */

passwd結構體說明:
passwd Member Description
char *pw_name          The user's login name
uid_t pw_uid             The UID number
gid_t pw_gid             The GID number
char *pw_dir            The user's home directory
char *pw_gecos        The user's full name
char *pw_shell         The user's default shell

示例代碼:
#include <stdio.h>
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>

int main(void)
{
        uid_t uid;
        gid_t gid;
        struct passwd *pw;

        uid = getuid();
        gid = getgid();
        pw = getpwuid(uid);

        printf("User is %s/n", getlogin());
        printf("The uid is:%d/n", uid);
        printf("The gid is:%d/n",gid);
        printf("The pw struct:/n name=%s, uid=%d, gid=%d, home=%s,shell=%s/n", pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell);
}

用gethostname()函數獲取主機名。

函數聲明:
#include <unistd.h>
int gethostname(char *name, size_t namelen);    /* 主機名返回給name變量 */

示例代碼:
#include <stdio.h>
#include <unistd.h>

int main(void)
{
        char computer[100];
        int status;

        status = gethostname(computer, 100);
        printf("The status is %d/n", status);
        printf("The hostname is: %s/n", computer);
}

用uname()函數獲取主機詳細信息,就像shell的uname命令返回的信息一樣。

函數聲明:
#include <sys/utsname.h>
int uname(struct utsname *name);

utsname結構體說明:
utsname Member                  Description
char sysname[]                  The operating system name
char nodename[]                 The host name
char release[]                  The release level of the system
char version[]                  The version number of the system
char machine[]                  The hardware type

示例代碼:
#include <stdio.h>
#include <unistd.h>
#include <sys/utsname.h>

int main(void)
{
        char computer[100];
        int status;
        struct utsname uts;

        status = gethostname(computer,100);
        printf("The computer's size is %d/n",sizeof(computer));
        printf("The status is %d/n", status);
        printf("The hostname is: %s/n", computer);

        uname(&uts);
        printf("The uname's information./n uts.sysname=%s/n uts.machine=%s/n uts.nodename=%s/n uts.release=%s/n uts.version=%s/n", uts.sysname,uts.machine,uts.nodename,uts.release,uts.version);
}

6.6. 日誌信息

使用syslog()函數處理日誌信息。

函數聲明:
#include <syslog.h>
void syslog(int priority, const char *message, arguments...);

priority參數的格式(severity level|facility code)
示例:
LOG_ERR|LOG_USER

severity level:
Priority Level               Description
LOG_EMERG                    An emergency situation
LOG_ALERT                    High-priority problem, such as database corruption
LOG_CRIT                     Critical error, such as hardware failure
LOG_ERR                      Errors
LOG_WARNING                  Warning
LOG_NOTICE                   Special conditions requiring attention
LOG_INFO                     Informational messages
LOG_DEBUG                    Debug messages

facility value(轉自syslog.h頭文件):
/* facility codes */
#define LOG_KERN        (0<<3)  /* kernel messages */
#define LOG_USER        (1<<3)  /* random user-level messages */
#define LOG_MAIL        (2<<3)  /* mail system */
#define LOG_DAEMON      (3<<3)  /* system daemons */
#define LOG_AUTH        (4<<3)  /* security/authorization messages */
#define LOG_SYSLOG      (5<<3)  /* messages generated internally by syslogd */
#define LOG_LPR         (6<<3)  /* line printer subsystem */
#define LOG_NEWS        (7<<3)  /* network news subsystem */
#define LOG_UUCP        (8<<3)  /* UUCP subsystem */
#define LOG_CRON        (9<<3)  /* clock daemon */
#define LOG_AUTHPRIV    (10<<3) /* security/authorization messages (private) */
#define LOG_FTP         (11<<3) /* ftp daemon */

示例代碼:
#include <syslog.h>
#include <stdio.h>

int main(void)
{
        FILE *f;

        f = fopen("abc","r");
        if(!f)                                   
                syslog(LOG_ERR|LOG_USER,"test - %m/n");      
}

上面的日誌信息由系統自動給出,我們也可過濾日誌信息。用到以下函數:

#include <syslog.h>
void closelog(void);
void openlog(const char *ident, int logopt, int facility);
int setlogmask(int maskpri);

logopt參數的選項:
logopt Parameter    Description
LOG_PID             Includes the process identifier, a unique number allocated to each process by the system, in the messages.
LOG_CONS            Sends messages to the console if they can’t be logged.
LOG_ODELAY          Opens the log facility at first call to .
LOG_NDELAY          Opens the log facility immediately, rather than at first log.

示例代碼:
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
        int logmask;

        openlog("logmask", LOG_PID|LOG_CONS, LOG_USER); /*日誌信息會包含進程id。*/
        syslog(LOG_INFO, "informative message, pid=%d", getpid());
        syslog(LOG_DEBUG,"debug message, should appear");   /*記錄該日誌信息。*/
        logmask = setlogmask(LOG_UPTO(LOG_NOTICE));     /*設置屏蔽低於NOTICE級別的日誌信息。*/
        syslog(LOG_DEBUG, "debug message, should not appear");  /*該日誌信息被屏蔽,不記錄。*/
}

不同安全級別的日誌信息存放在/var/log目錄下的哪個文件中是由/etc/syslog.conf文件控制的,下面是我係統中syslog.conf文件的內容:

#  /etc/syslog.conf     Configuration file for syslogd.
#
#                       For more information see syslog.conf(5)
#                       manpage.

#
# First some standard logfiles.  Log by facility.
#

auth,authpriv.*                 /var/log/auth.log
*.*;auth,authpriv.none          -/var/log/syslog
#cron.*                         /var/log/cron.log
daemon.*                        -/var/log/daemon.log
kern.*                          -/var/log/kern.log
lpr.*                           -/var/log/lpr.log
mail.*                          -/var/log/mail.log
user.*                          -/var/log/user.log
uucp.*                          /var/log/uucp.log

#
# Logging for the mail system.  Split it up so that
# it is easy to write scripts to parse these files.
#
mail.info                       -/var/log/mail.info
mail.warn                       -/var/log/mail.warn
mail.err                        /var/log/mail.err

# Logging for INN news system
#
news.crit                       /var/log/news/news.crit
news.err                        /var/log/news/news.err
news.notice                     -/var/log/news/news.notice

#
# Some `catch-all' logfiles.
#
*.=debug;/
        auth,authpriv.none;/
        news.none;mail.none     -/var/log/debug
*.=info;*.=notice;*.=warn;/
        auth,authpriv.none;/
        cron,daemon.none;/
        mail,news.none          -/var/log/messages

#
# Emergencies are sent to everybody logged in.
#
*.emerg                         *

#
# I like to have messages displayed on the console, but only on a virtual
# console I usually leave idle.
#
#daemon,mail.*;/
#       news.=crit;news.=err;news.=notice;/
#       *.=debug;*.=info;/
#       *.=notice;*.=warn       /dev/tty8

# The named pipe /dev/xconsole is for the `xconsole' utility.  To use it,
# you must invoke `xconsole' with the `-file' option:
#
#    $ xconsole -file /dev/xconsole [...]
#
# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
#      busy site..
#
daemon.*;mail.*;/
        news.crit;news.err;news.notice;/
        *.=debug;*.=info;/
        *.=notice;*.=warn       |/dev/xconsole

Chapter 7. 進程

Table of Contents

7.1. 進程狀態

進程是任何正在運行的程序代碼,它是操作系統的基本調度單位,只有它能在CPU上運行。對於一個進程,內核記錄以下信息:

    *

      進程運行的當前位置。
    *

      進程正在訪問的文件。
    *

      進程的所屬的用戶和組。
    *

      進程的當前目錄。
    *

      進程訪問的內存空間狀況。

7.1. 進程狀態

pid 是進程的標識符,存放在pid_t結構的變量中。在一個進程中創建另一個進程時,這個新進程就是子進程,原來的進程就是父進程。子進程結束時會通知父進程。如果父進程結束而子進程沒有結束,則子進程會成爲孤兒進程。所有孤兒進程都會變成init進程的子進程。init進程是系統啓動的第一個進程,它其中一個主要功能就是收集孤兒進程,以便內核將子進程從進程表中刪除。通過getpid()和getppid()函數可以獲得進程的pid。
Chapter 8. 串口編程

Table of Contents

8.1. 常用函數
8.2. 設置串口屬性
8.3. c_iflag輸入標誌說明
8.4. c_oflag輸出標誌說明
8.5. c_cflag控制模式標誌說明
8.6. c_cc[]控制字符說明
8.7. c_lflag本地模式標誌說明
8.8. 下面介紹一些常用串口屬性的設置方法。

8.1. 常用函數

使用open()函數打開串口,open()函數有兩個參數,第一個是要打開的設備名(如:/dev/ttyS0)。第二個是打開的方式。打開方式有以下三種:

    *

      O_RDWR,表示以讀寫方式打開串口。
    *

      O_NOCTTY,表示不成爲端口的控制終端,如果沒有這個選項,則任何輸入(鍵盤按鍵)都會中斷程序的執行。
    *

      O_NDELAY,表示程序不會關注DCD信號線所處的狀態,即不管對端設備是運行或掛起。如果沒有該選項,則程序會被設置成睡眠狀態,直到DCD信號爲低爲止。

成功打開串口則會返回文件描述符,打開失敗則返回-1。下面是一個打開串口的示例:

fd = open("/dev/ttyS0",O_RDWR|O_NDELAY|O_NDELAY);

使用close()關閉打開的串口,唯一的參數是打開串口的文件描述符。下面是一個關閉串口的示例:

close(fd);        //fd是打開串口返回的文件描述符

用write()函數向串口寫數據。下面是一個向串口寫數據的示例:

n = write(fd,buff,len);   
/* n表示成功寫到串口的字節數,如果寫入失敗則返回-1
   fd是打開串口返回的文件描述符
   buff表示寫入的內容
   len表示寫入信息的長度。
*/

用read()函數從串口讀取數據。下面是一個從串口讀數據的示例:

n = read(fd,buff,len);
/* n表示從串口讀到字節數
   fd是文件描述符
   buff是讀入字節存放的緩衝區
   len表示讀入的字節數
*/

通過fcntl()函數可以操作文件描述符,用以控制讀取數據的狀態。fcntl(fd,F_SETFL,0)表示沒有數據則阻塞,處於等待狀態,直到有數據到來;fcntl(fd,F_SETFL,FNDELAY)表示當端口沒有數據時馬上返回0。
8.2. 設置串口屬性

所有的串口屬性都在一個名爲termios的結構體中,要使用該結構體要包含termios.h頭文件。在該頭文件中還定義兩個重要的函數 tcgetattr()和tcsetattr(),分別用以獲取和設置串口的屬性。如:tcgetattr(fd,&old_termios), tcsetattr(fd,TCSANOW,&new_termios)。old_termios是舊的串口屬性,new_termios是重新設置的新串口屬性。tcsetattr()函數中常量的意義是:

    *

      TCSANOW表示新設置的串口屬性馬上生效。
    *

      TCSADRAIN表示等所有數據傳送完成後才生效。
    *

      TCSAFLUSH表示馬上清空輸入和輸出緩存,然後應用新的串口設置。

termios結構體內容:

成員            描述
-------------------------------------------
c_cflag         控制模式標誌
c_lflag         本地模式標誌
c_iflag         輸入模式標誌
c_oflag         輸出模式標誌
c_line          line discipline
c_cc[NCCS]      控制字符
c_ispeed        輸入波特率
c_ospeed        輸出波特率

在termios結構中的四個標誌控制了輸入輸出的四個不同部份。輸入模式標誌c_iflag決定如何解釋和處理接收的字符。輸出模式標誌c_oflag決定如何解釋和處理髮送到tty設備的字符。控制模式標誌決定設備的一系列協議特徵,這一標誌只對物理設備有效。本地模式標誌 c_lflag決定字符在輸出前如何收集和處理。

在串口傳輸中,用波特率來表示傳輸的速度,1波特表示在1秒鐘內可以傳輸1個碼元。波特率設置可以使用cfsetispeed(&new_termios,B19200)和cfsetospeed(&new_termios, B19200)這兩個函數來完成,默認的波特率爲9600baud。cfsetispeed()函數用來設置輸入的波特率,cfsetospeed()函數用來設置輸出的波特率。B19200是termios.h頭文件裏定義的一個宏,表示19200的波特率。

CLOCAL和CREAD是c_cflag成員中與速率相關的標誌,在串口編程中,這兩個標誌一定要有效,以確保程序在突發的作業控制或掛起時,不會成爲端口的佔有都,同時串口的接收驅動會自動讀入數據。設置方法如下:

termios_new.c_cflag |= CLOCAL;                 //保證程序不會成爲端的佔有者
termios_new.c_cflag |= CREAD;                  //使端口能讀取輸入的數據

設置串口屬性不能直接賦值,要通過對termios不同成員進行"與"和"或"操作來實現。在termios.h文件,定義了各種常量,如上面介紹的CLOCAL,CREAD。這些常量的值是掩碼,通過把這些常量與termios結構成員進行邏輯操作就可實現串口屬性的設置。在編程時用"|="來啓用屬性,用"&=~"來取消屬性。
8.3. c_iflag輸入標誌說明

    *

      BRKINT和IGNBRK

      如果設置了IGNBRK,中斷條件被忽略。如果沒有設置IGNBRK而設置了BRKINT,中斷條件清空輸入輸出隊列中所有的數據並且向tty的前臺進程組中所有進程發送一個SIGINT信號。如果這兩個都沒有設置,中斷條件會被看作一個0字符。這時,如果設置了PARMRK,當檢測到一個幀誤差時將會嚮應用程序發送三個字節'/377''/0''/0',而不是隻發送一個'/0'。
    *

      PARMRK和IGNPAR

      如果設定了IGNPAR,則忽略接收到的數據的奇偶檢驗錯誤或幀錯誤(除了前面提到的中斷條件)。如果沒有設置IGNPAR而設置了PARMRK,當接收到的字節存在奇偶檢驗錯誤或幀錯誤的時候。將嚮應用程序發送一個三字節的'/377''/0''/n'錯誤報告。其中n表示所接收到的字節。如果兩者都沒有設置,除了接收到的字節存在奇偶檢驗錯誤或幀誤差之外的中止條件都會嚮應用程序發送一個單字節('/0')的報告。
    *

      INPCK

      如果設置,則進行奇偶校驗。如果不進行奇偶檢驗,PARMRK和IGNPAR將對存在的奇偶校驗錯誤不產生任何的影響。
    *

      ISTRIP

      如果設置,所接收到的所有字節的高位將會被去除,保證它們是一個7位的字符。
    *

      INLCR

      如果設置,所接收到的換行字符('/n')將會被轉換成回車符('/r')。
    *

      IGNCR

      如果設置,則會忽略所有接收的回車符('/r')。
    *

      ICRNL

      如果設置,但IGNCR沒有設置,接收到的回車符嚮應用程序發送時會變換成換行符。
    *

      IUCLC

      如果IUCLC和IEXTEN都設置,接收到的所有大寫字母發送給應程序時都被轉換成小寫字母。POSIX中沒有定義該標記。
    *

      IXOFF

      如果設置,爲避免tty設備的輸入緩衝區溢出,tty設備可以向終端發送停止符^S和開始符^Q,要求終端停止或重新開始向計算機發送數據。通過停止符和開始符來控制數據流的方式叫軟件流控制,軟件流控制方式較少用,我們主要還是用硬件流控制方式。硬件流控制在c_cflag標誌中設置。
    *

      IXON

      如果設置,接收到^S後會停止向這個tty設備輸出,接收到^Q後會恢復輸出。
    *

      IXANY

      如果設置,則接到任何字符都會重新開始輸出,而不僅僅是^Q字符。
    *

      IMAXBEL

      如果設置,當輸入緩衝區空間滿時,再接收到的任何字符就會發出警報符'/a'。POSIX中沒有定義該標記。

8.4. c_oflag輸出標誌說明

OPOST是POSIX定義的唯一一個標誌,只有設置了該標誌後,其它非POSIX的輸出標記纔會生效。

    *

      OPOST

      開啓該標記,後面的輸出標記纔會生效。否則,不會對輸出數據進行處理。
    *

      OLCUC

      如果設置,大寫字母被轉換成小寫字母輸出。
    *

      ONLCR

      如果設置,在發送換行符('/n')前先發送回車符('/r')。
    *

      ONOCR

      如果設置,當current column爲0時,回車符不會被髮送也不會被處理。
    *

      OCRNL

      如果設置,回車符會被轉換成換行符。另外,如果設置了ONLRET,則current column會被設爲0.
    *

      ONLRET

      如果設置,當一個換行符或回車符被髮送的時候,current column會被設置爲0。
    *

      OXTABS

      如果設置,製表符會被轉換成空格符。

8.5. c_cflag控制模式標誌說明

    *

      CLOCAL

      如果設置,modem的控制線將會被忽略。如果沒有設置,則open()函數會阻塞直到載波檢測線宣告modem處於摘機狀態爲止。
    *

      CREAD

      只有設置了才能接收字符,該標記是一定要設置的。
    *

      CSIZE

      設置傳輸字符的位數。CS5表示每個字符5位,CS6表示每個字符6位,CS7表示每個字符7位,CS8表示每個字符8位。
    *

      CSTOPB

      設置停止位的位數,如果設置,則會在每幀後產生兩個停止位,如果沒有設置,則產生一個停止位。一般都是使用一位停止位。需要兩位停止位的設備已過時了。
    *

      HUPCL

      如果設置,當設備最後打開的文件描述符關閉時,串口上的DTR和RTS線會減弱信號,通知Modem掛斷。也就是說,當一個用戶通過Modem拔號登錄系統,然後註銷,這時Modem會自動掛斷。
    *

      PARENB和PARODD

      如果設置PARENB,會產生一個奇偶檢驗位。如果沒有設置PARODD,則產生偶校驗位,如果設置了PARODD,則產生奇校驗位。如果沒有設置PARENB,則PARODD的設置會被忽略。
    *

      CRTSCTS

      使用硬件流控制。在高速(19200bps或更高)傳輸時,使用軟件流控制會使效率降低,這個時候必須使用硬件流控制。

8.6. c_cc[]控制字符說明

只有在本地模式標誌c_lflag中設置了IEXITEN時,POSIX沒有定義的控制字符才能在Linux中使用。每個控制字符都對應一個按鍵組合(^C、^H等),但VMIN和VTIME這兩個控制字符除外,它們不對應控制符。這兩個控制字符只在原始模式下才有效。

    *

      c_cc[VINTR]

      默認對應的控制符是^C,作用是清空輸入和輸出隊列的數據並且向tty設備的前臺進程組中的每一個程序發送一個SIGINT信號,對SIGINT信號沒有定義處理程序的進程會馬上退出。
    *

      c_cc[VQUIT]

      默認對應的控制符是^/,作用是清空輸入和輸出隊列的數據並向tty設備的前臺進程組中的每一個程序發送一個SIGQUIT信號,對SIGQUIT信號沒有定義處理程序的進程會馬上退出。
    *

      c_cc[verase]

      默認對應的控制符是^H或^?,作用是在標準模式下,刪除本行前一個字符,該字符在原始模式下沒有作用。
    *

      c_cc[VKILL]

      默認對應的控制符是^U,在標準模式下,刪除整行字符,該字符在原始模式下沒有作用。
    *

      c_cc[VEOF]

      默認對應的控制符是^D,在標準模式下,使用read()返回0,標誌一個文件結束。
    *

      c_cc[VSTOP]

      默認對應的控制字符是^S,作用是使用tty設備暫停輸出直到接收到VSTART控制字符。或者,如果設備了IXANY,則等收到任何字符就開始輸出。
    *

      c_cc[VSTART]

      默認對應的控制字符是^Q,作用是重新開始被暫停的tty設備的輸出。
    *

      c_cc[VSUSP]

      默認對應的控制字符是^Z,使當前的前臺進程接收到一個SIGTSTP信號。
    *

      c_cc[VEOL]和c_cc[VEOL2]

      在標準模式下,這兩個下標在行的末尾加上一個換行符('/n'),標誌一個行的結束,從而使用緩衝區中的數據被髮送,並開始新的一行。POSIX中沒有定義VEOL2。
    *

      c_cc[VREPRINT]

      默認對應的控制符是^R,在標準模式下,如果設置了本地模式標誌ECHO,使用VERPRINT對應的控制符和換行符在本地顯示,並且重新打印當前緩衝區中的字符。POSIX中沒有定義VERPRINT。
    *

      c_cc[VWERASE]

      默認對應的控制字符是^W,在標準模式下,刪除緩衝區末端的所有空格符,然後刪除與之相鄰的非空格符,從而起到在一行中刪除前一個單詞的效果。POSIX中沒有定義VWERASE。
    *

      c_cc[VLNEXT]

      默認對應的控制符是^V,作用是讓下一個字符原封不動地進入緩衝區。如果要讓^V字符進入緩衝區,需要按兩下^V。POSIX中沒有定義VLNEXT。

要禁用某個控制字符,只需把它設置爲_POSIX_VDISABLE即可。但該常量只在Linux中有效,所以如果程序要考慮移植性的問題,請不要使用該常量。
8.7. c_lflag本地模式標誌說明

    *

      ICANON

      如果設置,則啓動標準模式,如果沒有設置,則啓動原始模式。
    *

      ECHO

      如果設置,則啓動本地回顯。如果沒有設置,則除了ECHONL之外,其他以ECHO開頭的標記都會失效。
    *

      ECHOCTL

      如果設置,則以^C的形式打印控制字符,如:按Ctrl+C顯示^C,按Ctrl+?顯示^?。
    *

      ECHOE

      如果在標準模式下設定了ECHOE標誌,則當收到一個ERASE控制符時將刪除前一個顯示字符。
    *

      ECHOK和ECHOKE

      在標準模式下,當接收到一個KILL控制符,則在緩衝區中刪除當前行。如果ECHOK、ECHOKE和ECHOE都沒有設置,則用ECHOCTL表示的KILL字符(^U)將會在輸出終端上顯示,表示當前行已經被刪除。

      如果已經設置了ECHOE和ECHOK,但沒有設置ECHOKE,將會在輸出終端顯示ECHOCTL表示的KILL字符,緊接着是換行,如果設置了OPOST,將會通過OPOST處理程序進行適當的處理。

      如果ECHOK、ECHOKE和ECHOE都有設置,則會刪除當前行。

      在POSIX中沒有定義ECHOKE標記,在沒有定義ECHOKE標記的系統中,設置ECHOK則表示同時設置了ECHOKE標誌。
    *

      ECHONL

      如果在標準模式下設置了該標誌,即使沒有設置ECHO標誌,換行符還是會被顯示出來。
    *

      ECHOPRT

      如果設置,則字符會被簡單地打印出來,包括各種控制字符。在POSIX中沒有定義該標誌。
    *

      ISIG

      如果設置,與INTR、QUIT和SUSP相對應的信號SIGINT、SIGQUIT和SIGTSTP會發送到tty設備的前臺進程組中的所有進程。
    *

      NOFLSH

      一般情況下,當接收到INTR或QUIT控制符的時候會清空輸入輸出隊列,當接收到SUSP控制符時會清空輸入隊列。但是如果設置了NOFLUSH標誌,則所有隊列都不會被清空。
    *

      TOSTOP

      如果設置,則當一個非前臺進程組的進程試圖向它的控制終端寫入數據時,信號SIGTTOU會被被髮送到這個進程所在的進程組。默認情況下,這個信號會使進程停止,就像收到SUSP控制符一樣。
    *

      IEXIEN

      默認已設置,我們不應修改它。在Linux中IUCLC和幾個與刪除字符相關的標記都要求在設置了IEXIEN才能正常工作。

8.8. 下面介紹一些常用串口屬性的設置方法。

    *

      設置流控制

termios_new.c_cflag &= ~CRTSCTS;            //不使用流控制
termios_new.c_cflag |= CRTSCTS;                 //使用硬件流控制
termios_new.c_iflag |= IXON|IXOFF|IXANY;        //使用軟件流控制

    *

      屏蔽字符大小位

termios_new.c_cflag &= ~CSIZE;

    *

      設置數據位大小

termios_new.c_cflag |= CS8;         //使用8位數據位
termios_new.c_cflag |= CS7;         //使用7位數據位
termios_new.c_cflag |= CS6;         //使用6位數據位
termios_new.c_cflag |= CS5;         //使用5位數據位

    *

      設置奇偶校驗方式

termios_new.c_cflag &amp;= ~PARENB;       //無奇偶校驗

termios_new.c_cflag |= PARENB;            //奇校驗
termios_new.c_cflag &amp;= ~PARODD;      

termios_new.c_cflag |= PARENB;            //偶校驗
termios_new.c_cflag &amp;= ~PARODD;      

    *

      停止位

termios_new.c_cflag |= CSTOPB;            //2位停止位
termios_new.c_cflag &amp;= ~CSTOPB;       //1位停止位 

    *

      輸出模式

termios_new.c_cflag &amp;= ~OPOST;        //原始數據(RAW)輸出

    *

      控制字符

termios_new.c_cc[VMIN] = 1;               //讀取字符的最小數量
termios_new.c_cc[VTIME] = 1;              //讀取第一個字符的等待時間

    *

      關閉終端回顯,鍵盤輸入的字符不會在終端窗口顯示。

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

int main(void)
{
        struct termios ts,ots;
        char passbuf[1024];

        tcgetattr(STDIN_FILENO,&ts);  /* STDIN_FILENO的值是1,表示標準輸入的文件描述符 */
        ots = ts;

        ts.c_lflag &= ~ECHO;         /* 關閉回終端回顯功能*/
        ts.c_lflag |= ECHONL;
        tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts);  /* 應用新終端設置 */

        fgets(passbuf,1024,stdin);     /* 輸入字符不會在終端顯示 */
        printf("you input character = %s/n",passbuf);

        tcsetattr(STDIN_FILENO,TCSANOW,&ots);  /* 恢復舊的終端設備 */
}

Chapter 9. 安全

Table of Contents

9.1. 內核漏洞介紹

Linux內核以穩定和安全著稱,但隨着Linux使用範圍的不斷擴展,各種漏洞也慢慢被內核開發人員或黑客發現。這裏介紹有關Linux內核和基於Linux的開源軟件的安全問題。
9.1. 內核漏洞介紹

    *

      權限提升類
    *

      拒絕服務類
    *

      溢出類
    *

      IP地址欺騙類

Chapter 10. 數據結構(Data Structure)

Table of Contents

10.1. 基礎概念

10.1. 基礎概念

在實際解決問題的時候,各種數據都不是孤立的,數據之間總是存在關係,這種數據之間的關係叫做數據結構。我們可以把數據結構的形式歸併爲四種:

    *

      集合:數據之間沒有對應關係,但同屬於一個集合。如汽車是一個集合,編程語言也是一個集合。
    *

      線性結構:各數據有一一對應的關係,有前驅也有後續。
    *

      樹形結構:各數據間存在一對多的關係,有一個前驅但有多個後續。
    *

      圖:各數據間有多對多的關係,對前驅和後續沒有限制。

數據類型是一個值的集合和定義在這個值集上的一組操作的總稱。

數據類型可分兩類,一類是每個對象僅由單值組成,稱爲原子類型,如整型、字符型等。另一類是由某種結構組成的類型,叫結構類型,如數組、字符串等。

抽象數據結構(Abstract Data Type,ADT)是一種數據類型及在這個類型上定的一組合法的操作。

算法(Algorithm)是一個有窮規則(或語句、指令)的有序集合。通俗地說,就是計算機解決問題的過程。算法應具備以下幾個重要的特性:

    *

      輸入:一個算法有零個或多個輸入。
    *

      輸出:一個算法至少有一個輸出,這種輸出是同輸入有着某些特定關係的量。沒有輸出的算法是沒有意義的。
    *

      有窮性:一個算法必須總是在執行有窮步之後的結束,且每一步都在有窮時間內完成。
    *

      確定性:算法中每條指令的含義都必須明確,無二義性。對相同的輸入,必須有相同的結果。
    *

      可行性:算法中的每條指令的執行時間都是有限的。

描述算法的工具:自然語言、流程圖、形式化語言和程序設計語言。

由瑞士科學家Niklaus Wirthrn提出的計算機界公認的公式:算法 + 數據結構 = 程序

算法設計的要求:正確、可讀、健壯、快速、節省存儲空間。 

發佈了22 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章