linux 下C編程 大牛總結

在很多人的眼裏,C語言和linux常常是分不開的。這其中的原因很多,其中最重要的一部分我認爲是linux本身就是C語言的傑出作品。當然,linux操作系統本身對C語言的支持也是相當到位的。作爲一個真正的程序員來說,如果沒有在linux下面用C語言編寫過完整的程序,那麼只能說他對C語言本身的理解還相關膚淺,對系統本身的認識也不夠到位。作爲程序員來說,linux系統爲我們提供了很多理想的環境,這其中包括了下面幾個方面,

(1)完善的編譯環境,包括gcc、as、ld等編譯、鏈接工具
(2)強大的調試環境,主要是gdb工具
(3)豐富的自動編譯工具,主要是make工具
(4)多樣化的os選擇,ubuntu、redflag等等
(5)浩瀚的開源代碼庫

當然,不管我怎麼說,最終朋友們還是應該自己勇敢地跨出前進的第一步。如果還沒有過Linux編程經驗的朋友可以首先在自己的pc上面安裝一個虛擬機,然後就可以在shell下面編寫自己的C語言代碼了。

  1. #include <stdio.h>
  2. int main()
  3. {
  4. printf("hello!\n");
  5. return 1;
  6. }
編寫完上面的代碼後,你需要做的就是兩個步驟:1、輸入 gcc hello.c -o hello;2、輸入./hello。如果一切正常的話,此時你應該會在屏幕上看到一行hello的打印。如果你看到了,那麼恭喜你,你已經可以開始linux的c語言編程之旅了。

當然,我們不會滿足於這麼簡單的打印功能。下面就可以編寫一個簡單的迭代函數,
  1. #include <stdio.h>
  2. int iterate(int value)
  3. {
  4. if(1 == value)
  5. return 1;
  6. return iterate(value - 1) + value;
  7. }
  8. int main()
  9. {
  10. printf("%d\n", iterate(10));
  11. return 1;
  12. }
此時,同樣我們需要重複上面的步驟:1、輸入gcc hello.c -o hello;2、輸入./hello。當然此時如果一切OK的話,你就會看到屏幕會有55這個數的輸出。本來1到10的數據之和就是55, 這說明我們的程序是正確的。

當然, 還會有一些朋友對程序的反彙編感興趣,那麼他需要兩個步驟:1、gcc hello.c -g -o hello;2、objdump -S -d ./hello。之所以在gcc編譯的時候加上-g是爲了添加調試信息,objdump中的-S選項是爲了在顯示彙編代碼的時候同時顯示原來的C語言源代碼。
  1. int iterate(int value)
  2. {
  3. 8048374: 55 push %ebp
  4. 8048375: 89 e5 mov %esp,%ebp
  5. 8048377: 83 ec 08 sub $0x8,%esp
  6. if(1 == value)
  7. 804837a: 83 7d 08 01 cmpl $0x1,0x8(%ebp)
  8. 804837e: 75 09 jne 8048389 <iterate+0x15>
  9. return 1;
  10. 8048380: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp)
  11. 8048387: eb 16 jmp 804839f <iterate+0x2b>
  12. return iterate(value -1) + value;
  13. 8048389: 8b 45 08 mov 0x8(%ebp),%eax
  14. 804838c: 83 e8 01 sub $0x1,%eax
  15. 804838f: 89 04 24 mov %eax,(%esp)
  16. 8048392: e8 dd ff ff ff call 8048374 <iterate>
  17. 8048397: 8b 55 08 mov 0x8(%ebp),%edx
  18. 804839a: 01 c2 add %eax,%edx
  19. 804839c: 89 55 fc mov %edx,0xfffffffc(%ebp)
  20. 804839f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
  21. }
  22. 80483a2: c9 leave
  23. 80483a3: c3 ret


linux下的C語言開發(makefile編寫)

對於程序設計員來說,makefile是我們繞不過去的一個坎。可能對於習慣Visual C++的用戶來說,是否會編寫makefile無所謂。畢竟工具本身已經幫我們做好了全部的編譯流程。但是在Linux上面,一切變得不一樣了,沒有人會爲你做這一切。編代碼要靠你,測試要靠你,最後自動化編譯設計也要靠你自己。想想看,如果你下載了一個開源軟件,卻因爲自動化編譯失敗,那將會在很大程度上打擊你學習代碼的自信心了。所以,我的理解是這樣的。我們要學會編寫makefile,至少會編寫最簡單的makefile。

首先編寫add.c文件,

  1. #include "test.h"
  2. #include <stdio.h>
  3. int add(int a, int b)
  4. {
  5. return a + b;
  6. }
  7. int main()
  8. {
  9. printf(" 2 + 3 = %d\n", add(2, 3));
  10. printf(" 2 - 3 = %d\n", sub(2, 3));
  11. return 1;
  12. }
再編寫sub.c文件,
  1. #include "test.h"
  2. int sub(int a, int b)
  3. {
  4. return a - b;
  5. }
最後編寫test.h文件,
  1. #ifndef _TEST_H
  2. #define _TEST_H
  3. int add(int a, int b);
  4. int sub(int a, int b);
  5. #endif
那麼,就是這三個簡單的文件,應該怎麼編寫makefile呢?
  1. test: add.o sub.o
  2. gcc -o test add.o sub.o
  3. add.o: add.c test.h
  4. gcc -c add.c
  5. sub.o: sub.c test.h
  6. gcc -c sub.c
  7. clean:
  8. rm -rf test
  9. rm -rf *.o

linux下的C語言開發(gdb調試)

編寫代碼過程中少不了調試。在windows下面,我們有visual studio工具。在linux下面呢,實際上除了gdb工具之外,你沒有別的選擇。那麼,怎麼用gdb進行調試呢?我們可以一步一步來試試看。
  1. #include <stdio.h>
  2. int iterate(int value)
  3. {
  4. if(1 == value)
  5. return 1;
  6. return iterate(value - 1) + value;
  7. }
  8. int main()
  9. {
  10. printf("%d\n", iterate(10));
  11. return 1;
  12. }
既然需要調試,那麼生成的可執行文件就需要包含調試的信息,這裏應該怎麼做呢?很簡單,輸入 gcc test.c -g -o test。輸入命令之後,如果沒有編譯和鏈接方面的錯誤,你就可以看到 可執行文件test了。

調試的步驟基本如下所示,


(01) 首先,輸入gdb test
(02) 進入到gdb的調試界面之後,輸入list,即可看到test.c源文件
(03) 設置斷點,輸入 b main
(04) 啓動test程序,輸入run
(05) 程序在main開始的地方設置了斷點,所以程序在printf處斷住
(06) 這時候,可以單步跟蹤。s單步可以進入到函數,而n單步則越過函數
(07) 如果希望從斷點處繼續運行程序,輸入c
(08) 希望程序運行到函數結束,輸入finish
(09) 查看斷點信息,輸入 info break
(10) 如果希望查看堆棧信息,輸入bt
(11) 希望查看內存,輸入 x/64xh + 內存地址
(12) 刪除斷點,則輸入delete break + 斷點序號
(13) 希望查看函數局部變量的數值,可以輸入print + 變量名

(14)希望修改內存值,直接輸入 print + *地址 = 數值
(15) 希望實時打印變量的數值,可以輸入display + 變量名
(16) 查看函數的彙編代碼,輸入 disassemble + 函數名
(17) 退出調試輸入quit即可

linux下的C語言開發(AT&T 彙編語言)

同樣是x86的cpu,但是卻可以用不同形式的彙編語言來表示。在window上面我們使用的更多是intel格式的彙編語言,而在Linux系統上面使用的更多的常常是AT&T格式的彙編語言。那什麼是AT&T格式的彙編代碼呢?我們可以寫一個試試看。
  1. .data
  2. message: .string "hello!\n"
  3. length = . - message
  4. .text
  5. .global _start
  6. _start:
  7. movl $length, %edx
  8. movl $message, %ecx
  9. movl $1, %ebx
  10. movl $4, %eax
  11. int $0x80
  12. movl $0, %ebx
  13. movl $1, %eax
  14. int $0x80
這是一個簡單的彙編文件,我們可以分兩步進行編譯。首先,輸入 as -gstabs -o hello.o hello.s, 接着輸入ld -o hello hello.o即可。爲了驗證執行文件是否正確,可以輸入./hello驗證一下。

在as命令當中,由於我們使用了-gstabs選項,因此在hello執行文件中是包含調試信息的。所以,如果想單步調試的朋友可以輸入gdb hello進行調試。

那麼,hello執行文件反彙編的代碼又是什麼樣的呢?我們可以輸入objdump -S -d hello查看一下。

  1. 08048074 <_start>:
  2. .text
  3. .global _start
  4. _start:
  5. movl $length, %edx
  6. 8048074: ba 08 00 00 00 mov $0x8,%edx
  7. movl $message, %ecx
  8. 8048079: b9 9c 90 04 08 mov $0x804909c,%ecx
  9. movl $1, %ebx
  10. 804807e: bb 01 00 00 00 mov $0x1,%ebx
  11. movl $4, %eax
  12. 8048083: b8 04 00 00 00 mov $0x4,%eax
  13. int $0x80
  14. 8048088: cd 80 int $0x80
  15. movl $0, %ebx
  16. 804808a: bb 00 00 00 00 mov $0x0,%ebx
  17. movl $1, %eax
  18. 804808f: b8 01 00 00 00 mov $0x1,%eax
  19. int $0x80
  20. 8048094: cd 80 int $0x80
  21. ret
  22. 8048096: c3 ret

linux下的C語言開發(靜態庫)

在我們編寫軟件的過程當中,少不了需要使用別人的庫函數。因爲大家知道,軟件是一個協作的工程。作爲個人來講,你不可能一個人完成所有的工作。另外,網絡上一些優秀的開源庫已經被業內廣泛接受,我們也沒有必要把時間浪費在這些重複的工作上面。

既然說到了庫函數,那麼一般來說庫函數分爲兩種方式:靜態庫和動態庫。兩者的區別其實很小,靜態庫是必須要鏈接到執行文件中去的,而動態庫是不需要鏈接到最後的執行文件中的。怎麼理解呢?也就是說,對於最後的執行文件而言,你是否刪除靜態庫無所謂。但是,一旦你刪除了動態庫,最後的執行文件就玩不轉了。

今天我們討論的問題是靜態庫。爲了顯示windows和linux創建靜態庫之間的差別,我們首先在windows上面利用Visual C++6.0創建一個靜態庫。源文件的代碼很簡單,

  1. #include "test.h"
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
頭文件代碼也不難,
  1. #ifndef _TEST_H
  2. #define _TEST_H
  3. int add(int a, int b);
  4. #endif

如果你需要在windows上面創建一個靜態庫,那麼你需要進行下面的操作,

(1)打開visual C++ 6.0工具,單擊【File】-> 【New】->【Projects】
(2)選擇【Win32 Static Library】,同時在【Project Name】寫上項目名稱,在【Location】選擇項目保存地址
(3)單擊【Ok】,繼續單擊【Finish】,再單擊【Ok】,這樣一個靜態庫工程就創建好了
(4)重新單擊【File】->【New】->【Files】,選擇【C++ Source Files】,
(5)選中【Add to pproject】,將源文件加入到剛纔創建的工程中去,在File中輸入文件名+.c後綴
(6)重複4、5的操作,加入一個文件名+.h頭文件
(7)分別在頭文件和源文件中輸入上面的代碼,單擊F7按鈕,即可在Debug目錄中生成*.lib靜態庫文件

那麼,在linux下面應該怎麼運行呢?其實很簡單,兩條命令解決,
(1)首先生成*.o文件,輸入gcc -c test.c -o test.o
(2)利用ar命令生成靜態庫,輸入ar rc libtest.a test.o

此時如果還有一個hello.c文件使用到了這個靜態庫,比如說 ,
  1. #include <stdio.h>
  2. #include "test.h"
  3. int main()
  4. {
  5. printf("%d\n", add(2, 3));
  6. return 1;
  7. }

其實也很簡單,輸入一個簡單的命令就可以生成執行文件了,
(1)首先輸入gcc hello.c -o hello ./libtest.a

(2)輸入./hello,驗證生成的執行文件是否正確

(3)朋友們可以刪除libtest.a文件,重新輸入./hello,驗證執行文件是否可以正常運行

linux下的C語言開發(動態庫)

動態鏈接庫不是linux獨有的特性,在windows下面也存在這樣的特性。一般來說,windows下面的動態連接庫是以*.dll作爲結尾的,而linux下面的動態連接庫是以*.so結尾的。和靜態鏈接庫相比,動態連接庫可以共享內存資源,這樣可以減少內存消耗。另外,動態連接是需要經過操作系統加載器的幫助才能被普通執行文件發現的,所以動態連接庫可以減少鏈接的次數。有了這個特點,我們就不難發現爲什麼很多軟件的補丁其實都是以動態庫發佈的。

那麼,在Linux上動態庫是怎麼生成的呢?

  1. #include "test.h"
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
頭文件格式,
  1. #ifndef _TEST_H
  2. #define _TEST_H
  3. int add(int a, int b);
  4. #endif
此時如果我們想要生成動態庫,要做的工作其實非常簡單,輸入gcc -shared -fPIC -o libtest.so test.c即可。回車後輸入ls,我們就可以發現當前目錄下面出現了libtest.so文件。
  1. #include <stdio.h>
  2. #include "test.h"
  3. int main()
  4. {
  5. printf("%d\n", add(2, 3));
  6. return 1;
  7. }
在上面的代碼當中,我們發現使用到了add函數,那麼此時如何才能生成一個執行文件呢?也很簡單,輸入gcc hello.c -o hello ./libtest.so。然後輸入./hello,此時可以驗證一下執行文件運行是否正確。在編寫靜態庫的時候,我說過靜態庫是彙編鏈接到執行文件當中的,而動態庫不會。朋友們可以做個小實驗,刪除libtest.so,然後輸入./hello。此時大家可以看看系統有沒有錯誤返回?
這個時候,有的朋友就會問了,那在windows下面dll應該怎麼編寫呢?其實也不難,只要在test.h上面稍作改變即可。其他的步驟和靜態庫的操作是基本類似的。
  1. #ifndef _TEST_H
  2. #define _TEST_H
  3. #ifdef USR_DLL
  4. #define DLL_API _declspec(dllexport)
  5. #else
  6. #define DLL_API _declspec(dllimport)
  7. #endif
  8. DLL_API int add(int a, int b);
  9. #endif

linux下的C語言開發(定時器)

定時器是我們需要經常處理的一種資源。那linux下面的定時器又是怎麼一回事呢?其實,在linux裏面有一種進程中信息傳遞的方法,那就是信號。這裏的定時器就相當於系統每隔一段時間給進程發一個定時信號,我們所要做的就是定義一個信號處理函數。
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <sys/time.h>
  4. #include <stdlib.h>
  5. #include <signal.h>
  6. static int count = 0;
  7. static struct itimerval oldtv;
  8. void set_timer()
  9. {
  10. struct itimerval itv;
  11. itv.it_interval.tv_sec = 1;
  12. itv.it_interval.tv_usec = 0;
  13. itv.it_value.tv_sec = 1;
  14. itv.it_value.tv_usec = 0;
  15. setitimer(ITIMER_REAL, &itv, &oldtv);
  16. }
  17. void signal_handler(int m)
  18. {
  19. count ++;
  20. printf("%d\n", count);
  21. }
  22. int main()
  23. {
  24. signal(SIGALRM, signal_handler);
  25. set_timer();
  26. while(count < 10000);
  27. exit(0);
  28. return 1;
  29. }

linux下的C語言開發(自動編譯工具)

在Linux下面,編寫makefile是一件辛苦的事情。因此,爲了減輕程序員編寫makefile的負擔,人們發明了autoconf和automake這兩個工具,可以很好地幫我們解決這個問題。
我們可以通過一個簡單的示例來說明如何使用配置工具。

(1)首先,編寫源文件hello.c。
  1. #include <stdio.h>
  2. int main(int argc, char** argv[])
  3. {
  4. printf("hello, world!\n");
  5. return 1;
  6. }
(2)接下來,我們需要創建一個Makefile.am,同時編寫上腳本。
  1. SUBDIRS=
  2. bin_PROGRAMS=hello
  3. hello_SOURCES=hello.c

(3)直接輸入autoscan,生成文件configure.scan,再改名爲configure.in。

修改腳本AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
爲AC_INIT(hello, 1.0, [email protected])

同時,在AC_CONFIG_HEADER([config.h])後面添加
AM_INIT_AUTOMAKE(hello, 0.1)

(4)依次輸入aclocal命令、autoheader命令

(5)創建4個文件,分別爲README、NEWS、AUTHORS和ChangeLog

(6)依次輸入automake -a、autoconf命令

(7)輸入./configure,生成最終的Makefile

(8)如果需要編譯,輸入make;如果需要安裝, 輸入make install;如果需要發佈軟件包,輸入make dist

linux下的C語言開發(進程創建)

在Linux下面,創建進程是一件十分有意思的事情。我們都知道,進程是操作系統下面享有資源的基本單位。那麼,在Linux下面應該怎麼創建進程呢?其實非常簡單,一個fork函數就可以搞定了。但是,我們需要清楚的是子進程與父進程之間除了代碼是共享的之外,堆棧數據和全局數據均是獨立的。
  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <math.h>
  5. #include <errno.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>
  8. int main()
  9. {
  10. pid_t pid;
  11. if(-1 == (pid = fork()))
  12. {
  13. printf("Error happened in fork function!\n");
  14. return 0;
  15. }
  16. if(0 == pid)
  17. {
  18. printf("This is child process: %d\n", getpid());
  19. }
  20. else
  21. {
  22. printf("This is parent process: %d\n", getpid());
  23. }
  24. return 0;
  25. }

linux下的C語言開發(進程等待)

所謂進程等待,其實很簡單。前面我們說過可以用fork創建子進程,那麼這裏我們就可以使用wait函數讓父進程等待子進程運行結束後纔開始運行。注意,爲了證明父進程確實是等待子進程運行結束後才繼續運行的,我們使用了sleep函數。但是,在linux下面,sleep函數的參數是秒,而windows下面sleep的函數參數是毫秒。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. int main(int argc, char* argv[])
  5. {
  6. pid_t pid;
  7. pid = fork();
  8. if(0 == pid)
  9. {
  10. printf("This is child process, %d\n", getpid());
  11. sleep(5);
  12. }
  13. else
  14. {
  15. wait(NULL);
  16. printf("This is parent process, %d\n", getpid());
  17. }
  18. return 1;
  19. }
下面,我們需要做的就是兩步,首先輸入gcc fork.c -o fork, 然後輸入./fork,就會在console下面獲得這樣的結果。
  1. [root@localhost fork]# ./fork
  2. This is child process, 2135
  3. This is parent process, 2134

linux下的C語言開發(信號處理)

信號處理是linux程序的一個特色。用信號處理來模擬操作系統的中斷功能,對於我們這些系統程序員來說是最好的一個選擇了。要想使用信號處理功能,你要做的就是填寫一個信號處理函數即可。一旦進程有待處理的信號處理,那麼進程就會立即進行處理。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. int value = 0;
  5. void func(int sig)
  6. {
  7. printf("I get a signal!\n");
  8. value = 1;
  9. }
  10. int main()
  11. {
  12. signal(SIGINT, func);
  13. while(0 == value)
  14. sleep(1);
  15. return 0;
  16. }

爲了顯示linux對signal的處理流程,我們需要進行兩個步驟。第一,輸入gcc sig.c -o sig, 然後輸入./sig即可;第二則重啓一個console窗口,輸入ps -aux | grep sig, 在獲取sig的pid之後然後輸入kill -INT 2082, 我們即可得到如下的輸出。

  1. [root@localhost fork]#./sig
  2. I get a signal!
  3. [root@localhost fork]#

linux下的C語言開發(管道通信)

Linux系統本身爲進程間通信提供了很多的方式,比如說管道、共享內存、socket通信等。管道的使用十分簡單,在創建了匿名管道之後,我們只需要從一個管道發送數據,再從另外一個管道接受數據即可。
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. int pipe_default[2];
  6. int main()
  7. {
  8. pid_t pid;
  9. char buffer[32];
  10. memset(buffer, 0, 32);
  11. if(pipe(pipe_default) < 0)
  12. {
  13. printf("Failed to create pipe!\n");
  14. return 0;
  15. }
  16. if(0 == (pid = fork()))
  17. {
  18. close(pipe_default[1]);
  19. sleep(5);
  20. if(read(pipe_default[0], buffer, 32) > 0)
  21. {
  22. printf("Receive data from server, %s!\n", buffer);
  23. }
  24. close(pipe_default[0]);
  25. }
  26. else
  27. {
  28. close(pipe_default[0]);
  29. if(-1 != write(pipe_default[1], "hello", strlen("hello")))
  30. {
  31. printf("Send data to client, hello!\n");
  32. }
  33. close(pipe_default[1]);
  34. waitpid(pid, NULL, 0);
  35. }
  36. return 1;
  37. }
下面我們就可以開始編譯運行了,老規矩分成兩步驟進行:(1)輸入gcc pipe.c -o pipe;(2)然後輸入./pipe,過一會兒你就可以看到下面的打印了。
  1. [test@localhost pipe]$ ./pipe
  2. Send data to client, hello!
  3. Receive data from server, hello!

linux下的C語言開發(多線程編程)

多線程和多進程還是有很多區別的。其中之一就是,多進程是linux內核本身所支持的,而多線程則需要相應的動態庫進行支持。對於進程而言,數據之間都是相互隔離的,而多線程則不同,不同的線程除了堆棧空間之外所有的數據都是共享的。說了這麼多,我們還是自己編寫一個多線程程序看看結果究竟是怎麼樣的。
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. void func_1(void* args)
  6. {
  7. while(1){
  8. sleep(1);
  9. printf("this is func_1!\n");
  10. }
  11. }
  12. void func_2(void* args)
  13. {
  14. while(1){
  15. sleep(2);
  16. printf("this is func_2!\n");
  17. }
  18. }
  19. int main()
  20. {
  21. pthread_t pid1, pid2;
  22. if(pthread_create(&pid1, NULL, func_1, NULL))
  23. {
  24. return -1;
  25. }
  26. if(pthread_create(&pid2, NULL, func_2, NULL))
  27. {
  28. return -1;
  29. }
  30. while(1){
  31. sleep(3);
  32. }
  33. return 0;
  34. }

和我們以前編寫的程序有所不同,多線程代碼需要這樣編譯,輸入gcc thread.c -o thread -lpthread,編譯之後你就可以看到thread可執行文件,輸入./thread即可。

  1. [test@localhost Desktop]$ ./thread
  2. this is func_1!
  3. this is func_2!
  4. this is func_1!
  5. this is func_1!
  6. this is func_2!
  7. this is func_1!
  8. this is func_1!
  9. this is func_2!
  10. this is func_1!
  11. this is func_1!

linux下的C語言開發(線程等待)

和多進程一樣,多線程也有自己的等待函數。這個等待函數就是pthread_join函數。那麼這個函數有什麼用呢?我們其實可以用它來等待線程運行結束。
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. void func(void* args)
  6. {
  7. sleep(2);
  8. printf("this is func!\n");
  9. }
  10. int main()
  11. {
  12. pthread_t pid;
  13. if(pthread_create(&pid, NULL, func, NULL))
  14. {
  15. return -1;
  16. }
  17. pthread_join(pid, NULL);
  18. printf("this is end of main!\n");
  19. return 0;
  20. }

編寫wait.c文件結束之後,我們就可以開始編譯了。首先你需要輸入gcc wait.c -o wait -lpthread,編譯之後你就可以看到wait可執行文件,輸入./wait即可。

  1. [test@localhost thread]$ ./thread
  2. this is func!
  3. this is end of main!

linux下的C語言開發(線程互斥)

對於編寫多線程的朋友來說,線程互斥是少不了的。在linux下面,編寫多線程常用的工具其實是pthread_mutex_t。本質上來說,它和Windows下面的mutex其實是一樣的,差別幾乎是沒有。希望對線程互斥進行詳細瞭解的朋友可以看這裏
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. static int value = 0;
  6. pthread_mutex_t mutex;
  7. void func(void* args)
  8. {
  9. while(1)
  10. {
  11. pthread_mutex_lock(&mutex);
  12. sleep(1);
  13. value ++;
  14. printf("value = %d!\n", value);
  15. pthread_mutex_unlock(&mutex);
  16. }
  17. }
  18. int main()
  19. {
  20. pthread_t pid1, pid2;
  21. pthread_mutex_init(&mutex, NULL);
  22. if(pthread_create(&pid1, NULL, func, NULL))
  23. {
  24. return -1;
  25. }
  26. if(pthread_create(&pid2, NULL, func, NULL))
  27. {
  28. return -1;
  29. }
  30. while(1)
  31. sleep(0);
  32. return 0;
  33. }
編寫mutex.c文件結束之後,我們就可以開始編譯了。首先你需要輸入gcc mutex.c -o mutex -lpthread,編譯之後你就可以看到mutex可執行文件,輸入./mutex即可。
  1. [test@localhost thread]$ ./mutex
  2. value = 1!
  3. value = 2!
  4. value = 3!
  5. value = 4!
  6. value = 5!
  7. value = 6!

linux下的C語言開發(網絡編程)

不管在Windows平臺下面還是在Linux平臺下面,網絡編程都是少不了的。在互聯網發達的今天,我們的生活基本上已經離不開網絡了。我們可以用網絡幹很多的事情,比如說IM聊天、FTP下載、電子銀行、網絡購物、在線遊戲、電子郵件的收發等等。所以說,對於一個軟件的開發者來說,如果說他不會進行網絡程序的開發,那真是難以想象的。


在開始介紹網絡編程的方法之前,我們可以回憶一下計算機網絡的相關知識。目前爲止,我們使用的最多網絡協議還是tcp/ip網絡。通常來說,我們習慣上稱爲tcp/ip協議棧。至於協議棧分成幾層,有兩種說法。一種是五層,一種是七層,我個人本身也比較傾向於五層的劃分方法。大家可以通過下面的圖看看協議棧是怎麼劃分的。


5、應用層
4、傳輸層
3、網絡層
2、數據鏈路層
1、物理層


網絡的不同層次實現網絡的不同功能。物理層主要實現報文的成幀處理;數據鏈路層完成對報文的優先級的管理,同時實現二層轉發和流量控制;網絡層實現路由和轉發的功能,一方面它需要實現對報文的fragment處理,另外一方面它還需要對路由信息進行處理和保存;傳輸層實現報文的發送和接受,它利用計數、時序、定時器、重發等機制實現對報文的準確發送,當然這都是tcp的發送機制,而udp一般是不保證報文正確發送和接收的;應用層就是根據傳輸層的端口信息調用不同的程序來處理傳輸的內容,端口8080是http報文,端口21是ftp報文等等。上面的邏輯稍顯複雜,朋友們可以這麼理解,

物理層關心的是如何把電氣信號變成一段報文;數據鏈路層關心的是mac地址、vlan、優先級等;網絡層關心的是ip地址,下一跳ip;傳輸層關心的是端口資源;應用層關心的是報文組裝、解析、渲染、存儲、執行等等。

目前關於tcp/ip完整協議棧的代碼很多,其中我認爲寫得比較好的還是linux內核/net/ipv4下面的代碼。如果朋友們對ipv6的代碼感興趣,也可以看看/net/ipv6的代碼。檔案如果朋友們對整個協議棧的代碼結構理解得不是很清楚,可以參考《linux網絡分析與開發》這本書。

當然,作爲應用層,我們的其實考慮的不用這麼複雜。對於網絡程序編寫人員來講,所有網絡的資源只要和一個socket關聯在一起就可以了。當然在socket可用之前,我們需要爲它配置端口信息和ip地址。配置完了之後,我們就可以慢慢等待報文的收發了。所以一般來說,作爲服務器端口的處理流程是這樣的,

a) 創建socket
b) 綁定socket到特定的ip地址
c) 對socket進行偵聽處理
d) 接受socket,表明有客戶端和服務器連接
e) 和客戶端循環收發報文
f) 關閉socket


作爲服務器程序而言,它要對特定的端口進行綁定和偵聽處理,這樣稍顯複雜。但是如果是編寫客戶端的程序,一切的一切就變得非常簡單了,

a) 創建socket
b) 鏈接服務器端地址
c) 和服務器端的socket收發報文


上面只是對網絡編程做了一個基本的介紹,但是好多的東西還是沒有涉及到,比如說:(1) 什麼時候該使用udp,什麼時候該使用tcp?(2) 如何把多線程和網絡編程聯繫在一起? (3) 如何把多進程和網絡編程聯繫在一起? (4) 如何利用select函數、epoll_create機制、非阻塞函數提高socket的併發處理效率? (5) linux內核是怎麼實現tcp/ip協議的? (6) 我們自己是否也可以實現協議的處理流程等等?

關於這些內容,我們會重新開闢一個主題,逐步進行分析和仿真處理。敬請期待。

轉載地址:http://blog.csdn.net/szu030606/article/details/8455578

 

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