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語言代碼了。
- #include?<stdio.h>??
- ??
- int?main()??
- {??
- ????printf("hello!\n");??
- ????return?1;??
- }????
? ? 當然,我們不會滿足于這么簡單的打印功能。下面就可以編寫一個簡單的迭代函數,
- #include?<stdio.h>??
- ??
- int?iterate(int?value)??
- {??
- ????if(1?==?value)??
- ????????return?1;??
- ????return?iterate(value?-?1)?+?value;??
- }??
- ??
- int?main()??
- {??
- ????printf("%d\n",?iterate(10));??
- ????return?1;??????
- }??
? ? 當然, 還會有一些朋友對程序的反匯編感興趣,那么他需要兩個步驟:1、gcc hello.c -g -o hello;2、objdump -S -d ./hello。之所以在gcc編譯的時候加上-g是為了添加調試信息,objdump中的-S選項是為了在顯示匯編代碼的時候同時顯示原來的C語言源代碼。
- int?iterate(int?value)??
- {??
- ?8048374:???????55??????????????????????push???%ebp??
- ?8048375:???????89?e5???????????????????mov????%esp,%ebp??
- ?8048377:???????83?ec?08????????????????sub????$0x8,%esp??
- ????if(1?==?value)??
- ?804837a:???????83?7d?08?01?????????????cmpl???$0x1,0x8(%ebp)??
- ?804837e:???????75?09???????????????????jne????8048389?<iterate+0x15>??
- ????????return?1;??
- ?8048380:???????c7?45?fc?01?00?00?00????movl???$0x1,0xfffffffc(%ebp)??
- ?8048387:???????eb?16???????????????????jmp????804839f?<iterate+0x2b>??
- ????return?iterate(value?-1)?+?value;??
- ?8048389:???????8b?45?08????????????????mov????0x8(%ebp),%eax??
- ?804838c:???????83?e8?01????????????????sub????$0x1,%eax??
- ?804838f:???????89?04?24????????????????mov????%eax,(%esp)??
- ?8048392:???????e8?dd?ff?ff?ff??????????call???8048374?<iterate>??
- ?8048397:???????8b?55?08????????????????mov????0x8(%ebp),%edx??
- ?804839a:???????01?c2???????????????????add????%eax,%edx??
- ?804839c:???????89?55?fc????????????????mov????%edx,0xfffffffc(%ebp)??
- ?804839f:???????8b?45?fc????????????????mov????0xfffffffc(%ebp),%eax??
- }??
- ?80483a2:???????c9??????????????????????leave??
- ?80483a3:???????c3??????????????????????ret??
- ???
linux下的C語言開發(makefile編寫)
? 對于程序設計員來說,makefile是我們繞不過去的一個坎。可能對于習慣Visual C++的用戶來說,是否會編寫makefile無所謂。畢竟工具本身已經幫我們做好了全部的編譯流程。但是在Linux上面,一切變得不一樣了,沒有人會為你做這一切。編代碼要靠你,測試要靠你,最后自動化編譯設計也要靠你自己。想想看,如果你下載了一個開源軟件,卻因為自動化編譯失敗,那將會在很大程度上打擊你學習代碼的自信心了。所以,我的理解是這樣的。我們要學會編寫makefile,至少會編寫最簡單的makefile。? ? 首先編寫add.c文件,
- #include?"test.h"??
- #include?<stdio.h>??
- ??
- int?add(int?a,?int?b)??
- {??
- ????return?a?+?b;??
- }??
- ??
- int?main()??
- {??
- ????printf("?2?+?3?=?%d\n",?add(2,?3));??
- ????printf("?2?-?3?=?%d\n",?sub(2,?3));??
- ????return?1;??
- }??
- #include?"test.h"??
- ??
- int?sub(int?a,?int?b)??
- {??
- ????return?a?-?b;??
- }??
- #ifndef?_TEST_H??
- #define?_TEST_H??
- ??
- int?add(int?a,?int?b);??
- int?sub(int?a,?int?b);??
- #endif??
- test:?add.o?sub.o??
- ????gcc?-o?test?add.o?sub.o??
- ??
- add.o:?add.c?test.h??
- ????gcc?-c?add.c??
- ??
- sub.o:?sub.c?test.h??
- ????gcc?-c?sub.c??????
- ??????
- clean:??
- ????rm?-rf?test??
- ????rm?-rf?*.o?
linux下的C語言開發(gdb調試)
? 編寫代碼過程中少不了調試。在windows下面,我們有visual studio工具。在linux下面呢,實際上除了gdb工具之外,你沒有別的選擇。那么,怎么用gdb進行調試呢?我們可以一步一步來試試看。- #include?<stdio.h>??
- ??
- int?iterate(int?value)??
- {??
- ????if(1?==?value)??
- ????????return?1;??
- ??
- ????return?iterate(value?-?1)?+?value;??
- }??
- ??
- int?main()??
- {??
- ????printf("%d\n",?iterate(10));??
- ????return?1;??
- }??
? ? 調試的步驟基本如下所示,
(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 + 變量名
(15) 希望實時打印變量的數值,可以輸入display + 變量名
(16) 查看函數的匯編代碼,輸入 disassemble + 函數名
(17) 退出調試輸入quit即可
linux下的C語言開發(AT&T 匯編語言)
? 同樣是x86的cpu,但是卻可以用不同形式的匯編語言來表示。在window上面我們使用的更多是intel格式的匯編語言,而在Linux系統上面使用的更多的常常是AT&T格式的匯編語言。那什么是AT&T格式的匯編代碼呢?我們可以寫一個試試看。- .data??
- ????message:?.string?"hello!\n"??
- ????length?=?.?-?message??
- ??
- .text??
- .global?_start??
- ??
- _start:??
- ????movl?$length,?%edx??
- ????movl?$message,?%ecx??
- ????movl?$1,?%ebx??
- ????movl?$4,?%eax??
- ????int?$0x80??
- ??
- ????movl?$0,?%ebx??
- ????movl?$1,?%eax??
- ????int?$0x80??
? ? 在as命令當中,由于我們使用了-gstabs選項,因此在hello執行文件中是包含調試信息的。所以,如果想單步調試的朋友可以輸入gdb hello進行調試。
? ? 那么,hello執行文件反匯編的代碼又是什么樣的呢?我們可以輸入objdump -S -d hello查看一下。
- 08048074?<_start>:??
- .text??
- .global?_start??
- ??
- _start:??
- ????????movl?$length,?%edx??
- ?8048074:???????ba?08?00?00?00??????????mov????$0x8,%edx??
- ????????movl?$message,?%ecx??
- ?8048079:???????b9?9c?90?04?08??????????mov????$0x804909c,%ecx??
- ????????movl?$1,?%ebx??
- ?804807e:???????bb?01?00?00?00??????????mov????$0x1,%ebx??
- ????????movl?$4,?%eax??
- ?8048083:???????b8?04?00?00?00??????????mov????$0x4,%eax??
- ????????int?$0x80??
- ?8048088:???????cd?80???????????????????int????$0x80??
- ??
- ????????movl?$0,?%ebx??
- ?804808a:???????bb?00?00?00?00??????????mov????$0x0,%ebx??
- ????????movl?$1,?%eax??
- ?804808f:???????b8?01?00?00?00??????????mov????$0x1,%eax??
- ????????int?$0x80??
- ?8048094:???????cd?80???????????????????int????$0x80??
- ????????ret??
- ?8048096:???????c3??????????????????????ret??
- ??????
linux下的C語言開發(靜態庫)
在我們編寫軟件的過程當中,少不了需要使用別人的庫函數。因為大家知道,軟件是一個協作的工程。作為個人來講,你不可能一個人完成所有的工作。另外,網絡上一些優秀的開源庫已經被業內廣泛接受,我們也沒有必要把時間浪費在這些重復的工作上面。? ? 既然說到了庫函數,那么一般來說庫函數分為兩種方式:靜態庫和動態庫。兩者的區別其實很小,靜態庫是必須要鏈接到執行文件中去的,而動態庫是不需要鏈接到最后的執行文件中的。怎么理解呢?也就是說,對于最后的執行文件而言,你是否刪除靜態庫無所謂。但是,一旦你刪除了動態庫,最后的執行文件就玩不轉了。
? ? 今天我們討論的問題是靜態庫。為了顯示windows和linux創建靜態庫之間的差別,我們首先在windows上面利用Visual C++6.0創建一個靜態庫。源文件的代碼很簡單,
- #include?"test.h"??
- ??
- int?add(int?a,?int?b)??
- {??
- ????return?a?+?b;??
- }??
- #ifndef?_TEST_H??
- #define?_TEST_H??
- ??
- int?add(int?a,?int?b);??
- #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文件使用到了這個靜態庫,比如說 ,
- #include?<stdio.h>??
- #include?"test.h"??
- ??
- int?main()??
- {??
- ????printf("%d\n",?add(2,?3));??
- ????return?1;??
- }??
? ? 其實也很簡單,輸入一個簡單的命令就可以生成執行文件了,
(1)首先輸入gcc hello.c -o hello ./libtest.a
(2)輸入./hello,驗證生成的執行文件是否正確
(3)朋友們可以刪除libtest.a文件,重新輸入./hello,驗證執行文件是否可以正常運行
linux下的C語言開發(動態庫)
動態鏈接庫不是linux獨有的特性,在windows下面也存在這樣的特性。一般來說,windows下面的動態連接庫是以*.dll作為結尾的,而linux下面的動態連接庫是以*.so結尾的。和靜態鏈接庫相比,動態連接庫可以共享內存資源,這樣可以減少內存消耗。另外,動態連接是需要經過操作系統加載器的幫助才能被普通執行文件發現的,所以動態連接庫可以減少鏈接的次數。有了這個特點,我們就不難發現為什么很多軟件的補丁其實都是以動態庫發布的。? ? 那么,在Linux上動態庫是怎么生成的呢?
- #include?"test.h"??
- ??
- int?add(int?a,?int?b)??
- {??
- ????return?a?+?b;??
- }??
- #ifndef??_TEST_H??
- #define??_TEST_H??
- ??
- int?add(int?a,?int?b);??
- #endif??
- #include?<stdio.h>??
- #include?"test.h"??
- ??
- int?main()??
- {??
- ????printf("%d\n",?add(2,?3));??
- ????return?1;??
- }??
? ? 這個時候,有的朋友就會問了,那在windows下面dll應該怎么編寫呢?其實也不難,只要在test.h上面稍作改變即可。其他的步驟和靜態庫的操作是基本類似的。
- #ifndef??_TEST_H??
- #define?_TEST_H??
- ??
- #ifdef?USR_DLL??
- #define?DLL_API?_declspec(dllexport)??
- #else??
- #define?DLL_API?_declspec(dllimport)??
- #endif??
- ??
- DLL_API?int?add(int?a,?int?b);??
- ??
- #endif?
linux下的C語言開發(定時器)
定時器是我們需要經常處理的一種資源。那linux下面的定時器又是怎么一回事呢?其實,在linux里面有一種進程中信息傳遞的方法,那就是信號。這里的定時器就相當于系統每隔一段時間給進程發一個定時信號,我們所要做的就是定義一個信號處理函數。- #include?<stdio.h>??
- #include?<time.h>??
- #include?<sys/time.h>??
- #include?<stdlib.h>??
- #include?<signal.h>??
- ??
- static?int?count?=?0;??
- static?struct?itimerval?oldtv;??
- ??
- void?set_timer()??
- {??
- ????struct?itimerval?itv;??
- ????itv.it_interval.tv_sec?=?1;??
- ????itv.it_interval.tv_usec?=?0;??
- ????itv.it_value.tv_sec?=?1;??
- ????itv.it_value.tv_usec?=?0;??
- ????setitimer(ITIMER_REAL,?&itv,?&oldtv);??
- }??
- ??
- void?signal_handler(int?m)??
- {??
- ????count?++;??
- ????printf("%d\n",?count);??
- }??
- ??
- int?main()??
- {??
- ????signal(SIGALRM,?signal_handler);??
- ????set_timer();??
- ????while(count?<?10000);??
- ????exit(0);??
- ????return?1;??
- }?
linux下的C語言開發(自動編譯工具)
? 在Linux下面,編寫makefile是一件辛苦的事情。因此,為了減輕程序員編寫makefile的負擔,人們發明了autoconf和automake這兩個工具,可以很好地幫我們解決這個問題。? ? 我們可以通過一個簡單的示例來說明如何使用配置工具。
(1)首先,編寫源文件hello.c。
- #include?<stdio.h>??
- ??
- int?main(int?argc,?char**?argv[])??
- {??
- ????printf("hello,?world!\n");??
- ????return?1;??
- }??
- SUBDIRS=??
- ??
- bin_PROGRAMS=hello??
- hello_SOURCES=hello.c????
(3)直接輸入autoscan,生成文件configure.scan,再改名為configure.in。
修改腳本AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
為AC_INIT(hello, 1.0, feixiaoxing@163.com)
同時,在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函數就可以搞定了。但是,我們需要清楚的是子進程與父進程之間除了代碼是共享的之外,堆棧數據和全局數據均是獨立的。- #include?<unistd.h>??
- #include?<stdio.h>??
- #include?<stdlib.h>??
- #include?<math.h>??
- #include?<errno.h>??
- #include?<sys/types.h>??
- #include?<sys/wait.h>??
- ??
- int?main()??
- {??
- ????pid_t?pid;??
- ??
- ????if(-1?==?(pid?=?fork()))??
- ????{??
- ????????printf("Error?happened?in?fork?function!\n");??
- ????????return?0;??
- ????}??
- ??
- ????if(0?==?pid)??
- ????{??
- ????????printf("This?is?child?process:?%d\n",?getpid());??
- ????}??
- ????else??
- ????{??
- ????????printf("This?is?parent?process:?%d\n",?getpid());??
- ????}??
- ??
- ????return?0;??
- }??
- ????
linux下的C語言開發(進程等待)
所謂進程等待,其實很簡單。前面我們說過可以用fork創建子進程,那么這里我們就可以使用wait函數讓父進程等待子進程運行結束后才開始運行。注意,為了證明父進程確實是等待子進程運行結束后才繼續運行的,我們使用了sleep函數。但是,在linux下面,sleep函數的參數是秒,而windows下面sleep的函數參數是毫秒。- #include?<stdio.h>??
- #include?<stdlib.h>??
- #include?<unistd.h>??
- ??
- int?main(int?argc,?char*?argv[])??
- {??
- ????pid_t?pid;??
- ??
- ????pid?=?fork();??
- ????if(0?==?pid)??
- ????{??
- ????????printf("This?is?child?process,?%d\n",?getpid());??
- ????????sleep(5);??
- ????}??
- ????else??
- ????{??
- ????????wait(NULL);??
- ????????printf("This?is?parent?process,?%d\n",?getpid());??
- ????}??
- ??
- ????return?1;??
- }??
- [root@localhost?fork]#?./fork??
- This?is?child?process,?2135??
- This?is?parent?process,?2134?
linux下的C語言開發(信號處理)
? 信號處理是linux程序的一個特色。用信號處理來模擬操作系統的中斷功能,對于我們這些系統程序員來說是最好的一個選擇了。要想使用信號處理功能,你要做的就是填寫一個信號處理函數即可。一旦進程有待處理的信號處理,那么進程就會立即進行處理。- #include?<stdio.h>??
- #include?<stdlib.h>??
- #include?<signal.h>??
- ??
- int?value?=?0;??
- ??
- void?func(int?sig)??
- {??
- ????printf("I?get?a?signal!\n");??
- ????value?=?1;??
- }??
- ??
- int?main()??
- {??
- ????signal(SIGINT,?func);??
- ??
- ????while(0?==?value)??
- ????????sleep(1);??
- ??
- ????return?0;??
- }??
? ? 為了顯示linux對signal的處理流程,我們需要進行兩個步驟。第一,輸入gcc sig.c -o sig, 然后輸入./sig即可;第二則重啟一個console窗口,輸入ps -aux | grep sig, 在獲取sig的pid之后然后輸入kill -INT 2082, 我們即可得到如下的輸出。
- [root@localhost?fork]#./sig??
- I?get?a?signal!??
- [root@localhost?fork]#?
linux下的C語言開發(管道通信)
? Linux系統本身為進程間通信提供了很多的方式,比如說管道、共享內存、socket通信等。管道的使用十分簡單,在創建了匿名管道之后,我們只需要從一個管道發送數據,再從另外一個管道接受數據即可。- #include?<stdio.h>??
- #include?<unistd.h>??
- #include?<stdlib.h>??
- #include?<string.h>??
- ??
- int?pipe_default[2];????
- ??
- int?main()??
- {??
- ????pid_t?pid;??
- ????char?buffer[32];??
- ??
- ????memset(buffer,?0,?32);??
- ????if(pipe(pipe_default)?<?0)??
- ????{??
- ????????printf("Failed?to?create?pipe!\n");??
- ????????return?0;??
- ????}??
- ??
- ????if(0?==?(pid?=?fork()))??
- ????{??
- ????????close(pipe_default[1]);??
- ????????sleep(5);??
- ????????if(read(pipe_default[0],?buffer,?32)?>?0)??
- ????????{??
- ????????????printf("Receive?data?from?server,?%s!\n",?buffer);??
- ????????}??
- ????????close(pipe_default[0]);??
- ????}??
- ????else??
- ????{??
- ????????close(pipe_default[0]);??
- ????????if(-1?!=?write(pipe_default[1],?"hello",?strlen("hello")))??
- ????????{??
- ????????????printf("Send?data?to?client,?hello!\n");??
- ????????}??
- ????????close(pipe_default[1]);??
- ????????waitpid(pid,?NULL,?0);??
- ????}??
- ??
- ????return?1;??
- }??
- [test@localhost?pipe]$?./pipe??
- Send?data?to?client,?hello!??
- Receive?data?from?server,?hello!?
linux下的C語言開發(多線程編程)
? 多線程和多進程還是有很多區別的。其中之一就是,多進程是linux內核本身所支持的,而多線程則需要相應的動態庫進行支持。對于進程而言,數據之間都是相互隔離的,而多線程則不同,不同的線程除了堆棧空間之外所有的數據都是共享的。說了這么多,我們還是自己編寫一個多線程程序看看結果究竟是怎么樣的。- #include?<stdio.h>??
- #include?<pthread.h>??
- #include?<unistd.h>??
- #include?<stdlib.h>??
- ??
- void?func_1(void*?args)??
- {??
- ????while(1){??
- ????????sleep(1);??
- ????????printf("this?is?func_1!\n");??
- ????}??
- }??
- ??
- void?func_2(void*?args)??
- {??
- ????while(1){??
- ????????sleep(2);??
- ????????printf("this?is?func_2!\n");??
- ????}??
- }??
- ??
- int?main()??
- {??
- ????pthread_t?pid1,?pid2;??
- ??
- ????if(pthread_create(&pid1,?NULL,?func_1,?NULL))??
- ????{??
- ????????return?-1;??
- ????}??
- ??
- ????if(pthread_create(&pid2,?NULL,?func_2,?NULL))??
- ????{??
- ????????return?-1;??
- ????}??
- ??
- ????while(1){??
- ????????sleep(3);??
- ????}??
- ??
- ????return?0;??
- }??
? ? 和我們以前編寫的程序有所不同,多線程代碼需要這樣編譯,輸入gcc thread.c -o thread -lpthread,編譯之后你就可以看到thread可執行文件,輸入./thread即可。
- [test@localhost?Desktop]$?./thread??
- this?is?func_1!??
- this?is?func_2!??
- this?is?func_1!??
- this?is?func_1!??
- this?is?func_2!??
- this?is?func_1!??
- this?is?func_1!??
- this?is?func_2!??
- this?is?func_1!??
- this?is?func_1!?
linux下的C語言開發(線程等待)
和多進程一樣,多線程也有自己的等待函數。這個等待函數就是pthread_join函數。那么這個函數有什么用呢?我們其實可以用它來等待線程運行結束。- #include?<stdio.h>??
- #include?<pthread.h>??
- #include?<unistd.h>??
- #include?<stdlib.h>??
- ??
- void?func(void*?args)??
- {??
- ????sleep(2);??
- ????printf("this?is?func!\n");??
- }??
- ??
- int?main()??
- {??
- ????pthread_t?pid;??
- ??
- ????if(pthread_create(&pid,?NULL,?func,?NULL))??
- ????{??
- ????????return?-1;??
- ????}??
- ??
- ????pthread_join(pid,?NULL);??
- ????printf("this?is?end?of?main!\n");??
- ??
- ????return?0;??
- }??
? ? 編寫wait.c文件結束之后,我們就可以開始編譯了。首先你需要輸入gcc wait.c -o wait -lpthread,編譯之后你就可以看到wait可執行文件,輸入./wait即可。
- [test@localhost?thread]$?./thread??
- this?is?func!??
- this?is?end?of?main!?
linux下的C語言開發(線程互斥)
對于編寫多線程的朋友來說,線程互斥是少不了的。在linux下面,編寫多線程常用的工具其實是pthread_mutex_t。本質上來說,它和Windows下面的mutex其實是一樣的,差別幾乎是沒有。希望對線程互斥進行詳細了解的朋友可以看這里。- #include?<stdio.h>??
- #include?<pthread.h>??
- #include?<unistd.h>??
- #include?<stdlib.h>??
- ??
- static?int?value?=?0;??
- pthread_mutex_t?mutex;??
- ??
- void?func(void*?args)??
- {??
- ????while(1)??
- ????{??
- ????????pthread_mutex_lock(&mutex);??
- ????????sleep(1);??
- ????????value?++;??
- ????????printf("value?=?%d!\n",?value);??
- ????????pthread_mutex_unlock(&mutex);??
- ????}??
- }??
- ??
- int?main()??
- {??
- ????pthread_t?pid1,?pid2;??
- ????pthread_mutex_init(&mutex,?NULL);??
- ??
- ????if(pthread_create(&pid1,?NULL,?func,?NULL))??
- ????{??
- ????????return?-1;??
- ????}??
- ??
- ????if(pthread_create(&pid2,?NULL,?func,?NULL))??
- ????{??
- ????????return?-1;??
- ????}??
- ??
- ????while(1)??
- ????????sleep(0);??
- ??
- ????return?0;??
- }??
- [test@localhost?thread]$?./mutex??
- value?=?1!??
- value?=?2!??
- value?=?3!??
- value?=?4!??
- value?=?5!??
- 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) 我們自己是否也可以實現協議的處理流程等等?
? ? 關于這些內容,我們會重新開辟一個主題,逐步進行分析和仿真處理。敬請期待。