wiki:wade/linuxProgramming

Version 17 (modified by wade, 16 years ago) (diff)

--

基礎 Linux Programming

安裝 GNU/Linux 開發手冊

  1. 安裝 manpages-dev 跟 glibc-doc-reference,其中 manpages-dev 提供常用 POSIX 的 C 函式庫 manpage,而 glibc-doc-reference 則提供 C 函式庫的 info 檔(內含更多範例程式)
    # sudo apt-get install manpages-dev glibc-doc-reference
    
  2. 更新 manpage
      mandb
    
  3. 常用語法
    • 查詢這所有 manpage 內「指令」的用法:
         man -a 指令
      

例如要查調 printf 對應到那些 manpages:

 man -a printf
  • 查詢特定 manpages 內「指令」用法:
       man [section] 指令
    

例如要查詢 (1) Executable programs or shell commands 類的 printf 的用法。

   man 1 printf

網路程序連線流程

  • Server:socket → blind → listen → accept
  • Client:socket → connect
  1. socket:
    • man socket
    • socket 可以視為管線 (pipe) 概念的延伸,只是它不止可以在本機電腦,還可以透過網路來傳輸資料。它與管線最大的不同在於它將程序區分為 server 與 client,可以進行一對一或多對一的資訊傳遞服務。
    • socket() 會在 linux 上產生一個檔案名稱,通常存在 /tmp 中,檔案名稱也就是服務的辨識子埠號 (port #),負責將連接請求繞到正確的伺服器處理程序。wade/tcp_port_list
  2. blind:
    • socket 命名必須透過 blind() 這個 system call 。而 server 就在這具名的 socket 上等待 client 的連結請求。
  3. listen:
    • listen() 這個 system call 是用來產生一個 queue ,接應連接請求。
  4. accept:
    • accept() 這個 system call 是讓 server 真正的接受連接請求,當 server 呼叫 accept() 時,會產生一個未具名 socket ,具此不具名的 socket 只用在與 client 的連接上,原本具名的 socket 則繼續用來等待其它客戶端的連結請求,並將連結請求存入 queue 中。
  5. connect:
    • client 利用 socket() 產生一個未具名的 socket ,再呼叫 connect() 連接指定 server 具名的 socket 位址。成功後可以使用檔案描述子 (file descriptor) 的方式進行資料雙向溝通,如 read()、 write()。

File Descriptors

可以想成他只是一個 index ,負責檔案的開啟與關閉,是一個 C type int。

  1. 特殊的 FD:
FD Name 功能描述
0 Standard Input (stdin) 標準輸入,如 keyboard
1 Standard Output (stdout) 標準輸出,如 monitor
2 Standard Error (stderr) 標準錯誤,特殊輸出

Pipe

  • 範例:pipe.c
    // parent process 借由 fork() 產生一個跟自己一樣的 child process
    // 兩個 processes 間藉由 pipe 來傳遞資料
    // child → pipe → parent ,由 child 寫入資料至 pipe , parent 再由 pipe 接收資料並寫入 stdout
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main(void)
    {
      int n, fd[2];
      pid_t pid;
      char line[255];
    
      // 建立 pipe
      // fd[1] → pipe → fd[0]
      // pipe 的形勢與 FIFO 類似 
      if (pipe(fd) < 0)
      {
        printf("Can't create pipe!\n");
      }
    
      // fork 創造一個 child process ,他與 parent 相同
      if ( (pid = fork()) < 0)
      {
        printf("fork error\n");
      }
      else if (pid == 0)
      { // child process = 0
        close(fd[0]);
        // 將字串寫入 pipe 內 2 次
        write(fd[1], "hello world\n",12);
        write(fd[1], "second time\n",12);
      }
      else
      { // parent process
        close(fd[1]);
        n = read(fd[0], line, 255);
        // 將結果寫出到 FD=1 (stdout)
        write(1,line,n);
        write(1,line,n);
    
      }
    }
    
    • FD[0] 負責 read pipe。FD[1] 負責 write pipe,換句話說,寫入 FD[1] 的資料可以由 FD[0] 讀取。
    • 寫入 FD[1] → [PIPE] → 讀取 FD[0]
    • 由於 FD 是 file descriptor, 非 file stream。所以為法使用 fread 和 fwrite。要使用 read 和 write 函式。
    • read() 會被 block 住,所以不用擔心 parent 會比 child 先結束,而導致 child 也跟著結束 (因為 child process 是依附著 parent process 而存在,依存關係可透過 pstree 查詢)。
    • 先使用 pipe(FD[2]),再使用 fork() 時,FD[2] 也會同時複製兩份,child 跟 parent 有各自的 FD[0]、FD[1],所以要記得關掉不必要的 FD ,兩個 processes 各自與 pipe 相接。

  • 範例:pipe2.c
    // parent process 借由 fork() 產生一個跟自己一樣的 child process
    // 兩個 processes 間藉由 pipe 來傳遞資料
    // child → stdout → fd[1] → pipe → fd[0] → stdin → parent ,由 child 寫入資料至 pipe , parent 再由 pipe 接收資料並寫入 stdout
    
    #include <unistd.h>
    #include <stdio.h>
    
    int main(void)
    {
      int n, fd[2];
      pid_t pid;
      // 建立 pipe
      if (pipe(fd) == 0)
      {
        // fork 創造一個 child process ,他與 parent 相同
        pid = fork();
        // 錯誤偵測,將結果寫入 stderr 中。
        if( pid == -1)
        {
          fprintf(stderr, "fore failure");
          exit(EXIT_FAILURE);
        }
    
        // child process PID = 0
        if (pid == 0)
        { 
          // 關閉標準輸入
          close(0);
          // 將讀取 pipe 的 fd[0] 與 標準輸入連起來,則從 pipe 來的資料會成為一個標準輸入
          dup(fd[0]);
          close(fd[0]);
          close(fd[1]);
          // 此時 more 會開起來等待標準輸入的資料,他會從標準輸入(pipe)讀取資料。
          execlp("more", "more",  NULL);
        }
        // parent process PID ≠ 0
        else
        { 
          // 關閉標準輸出
          close(1);
          // 將寫入 pipe 的 fd[1] 與 標準輸出結合,則所有的標準輸出會自動寫入 pipe 中
          dup(fd[1]);
          close(fd[0]);
          close(fd[1]); 
          // 執行 ls ,並將結果寫入標準輸出,而標準輸出已經與 pipe 結合,所以結果會寫入 pipe 中
          execlp("ls", "ls", "-l", NULL);
        }
      }
    }
    
    • process 將自己的 stdout 寫入 write pipe ,將讀取 read pipe 轉換為本身的 stdin

Reference