第6章 文件處理與進程控制_第1頁
第6章 文件處理與進程控制_第2頁
第6章 文件處理與進程控制_第3頁
第6章 文件處理與進程控制_第4頁
第6章 文件處理與進程控制_第5頁
已閱讀5頁,還剩146頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、第第6章章 文件處理與進程控制文件處理與進程控制本章要點本章要點1、文件描述符的概念、文件描述符的概念2、系統調用的基本概念、系統調用的基本概念 3、文件讀寫等處理方法、文件讀寫等處理方法4、進程的概念、進程的概念 5、進程控制與進程間的通信、進程控制與進程間的通信6、嵌入式、嵌入式Linux中對串口的操作中對串口的操作 6.1嵌入式嵌入式Linux的文件處理的文件處理6.1.1文件描述符及文件處理文件描述符及文件處理 1、文件及文件描述符、文件及文件描述符 由于在由于在Linux下設備和目錄都看作是文件,下設備和目錄都看作是文件,因此,因此,Linux中的文件有中的文件有4種類型:普通文種類

2、型:普通文件、目錄文件、鏈接文件和設備文件。件、目錄文件、鏈接文件和設備文件。 Linux的內核利用文件描述符訪問文件。文的內核利用文件描述符訪問文件。文件描述符是非負整數,它是一個索引值,件描述符是非負整數,它是一個索引值,并指向內核中每個進程打開文件的記錄表。并指向內核中每個進程打開文件的記錄表。當打開一個現存文件或新建一個文件時,當打開一個現存文件或新建一個文件時,內核會向進程返回一個文件描述符。當讀內核會向進程返回一個文件描述符。當讀寫文件時,也需要使用文件描述符來指定寫文件時,也需要使用文件描述符來指定待讀寫的文件。待讀寫的文件。 2、系統調用、系統調用 所謂系統調用是指操作系統提供

3、給用戶程序所謂系統調用是指操作系統提供給用戶程序調用的一組調用的一組“特殊特殊”接口,用戶程序可以通接口,用戶程序可以通過這組過這組“特殊特殊”接口來獲得操作系統內核提接口來獲得操作系統內核提供的服務。例如用戶可以通過進程控制相關供的服務。例如用戶可以通過進程控制相關的系統調用來創建進程、實現進程調度、進的系統調用來創建進程、實現進程調度、進程管理等。程管理等。 3、文件處理、文件處理Linux系統的文件處理,主要是指進行打開文系統的文件處理,主要是指進行打開文件、讀文件、寫文件及關閉文件等件、讀文件、寫文件及關閉文件等I/O操作。操作。大多數情況下,只需用到大多數情況下,只需用到5個函數:個

4、函數:open、read、write、lseek 和和close。這幾個函數。這幾個函數不需要經過緩沖就能立即執行,因此,被稱不需要經過緩沖就能立即執行,因此,被稱之為不帶緩存的之為不帶緩存的I/O操作,即每一個函數都只操作,即每一個函數都只調用內核中的一個系統調用。調用內核中的一個系統調用。 6.1.2open函數和函數和close函數函數 1、open函數函數open函數用于打開或創建文件。函數用于打開或創建文件。調用調用open函數所需要的頭文件如下:函數所需要的頭文件如下:#include #include 其函數為:其函數為:int open(const char *pathname

5、, int oflag, int perms ) ;函數返回值:若文件打開成功則返回文件描函數返回值:若文件打開成功則返回文件描述符,若出錯則返回述符,若出錯則返回-1。2、close函數函數close函數用于關閉一個打開的文件,所需要函數用于關閉一個打開的文件,所需要的頭文件為:的頭文件為: #include 其函數為:其函數為: int close(int fd);函數返回值:若成功為函數返回值:若成功為0,若出錯為,若出錯為-1。關閉文件函數關閉文件函數close的參數的參數fd為文件描述符。為文件描述符。3、示例、示例 【例例6-1】用可讀寫方式新建(打開)一個文用可讀寫方式新建(打開

6、)一個文件。件。 詳見教材詳見教材6.1.3read函數、函數、write函數和函數和lseek函數函數1、read函數函數 read函數從打開的文件中讀取數據。函數從打開的文件中讀取數據。 調用調用read函數所需要的頭文件:函數所需要的頭文件: # include 其函數原型為:其函數原型為:ssize_t read(int fd, void *buf, size_t count);函數返回值為讀到數據的字節數,若返回值函數返回值為讀到數據的字節數,若返回值為為0,則已經到達文件尾,若返回,則已經到達文件尾,若返回-1則為出錯。則為出錯。2、write函數函數write函數用于向打開的文件

7、實現寫入數據的操作。函數用于向打開的文件實現寫入數據的操作。寫操作的位置從文件的當前位移量處開始。若磁盤寫操作的位置從文件的當前位移量處開始。若磁盤已滿或超出該文件的長度,則已滿或超出該文件的長度,則write函數返回錯誤值。函數返回錯誤值。調用調用write函數所需要的頭文件:函數所需要的頭文件: #include 其函數原型為:其函數原型為:ssize_t write(int fd, void *buf, size_t count);函數返回值為已寫入數據的字節數,若返回函數返回值為已寫入數據的字節數,若返回-1則出則出錯。錯。 3、lseek函數函數 lseek函數用于在由指定的文件描述

8、符的文件中將文件函數用于在由指定的文件描述符的文件中將文件指針定位到相應的位置,以進行讀寫操作。指針定位到相應的位置,以進行讀寫操作。 調用調用lseek函數所需要的頭文件:函數所需要的頭文件: #include 其函數原型為:其函數原型為:off_t lseek(int fd, off_t offset, int whence);函數返回值為文件的當前位移,若返回函數返回值為文件的當前位移,若返回-1則出錯。則出錯。4、示例、示例【例例6-2】創建一新文件,然后對此文件進行創建一新文件,然后對此文件進行讀寫操作。讀寫操作。設計思路與分析:設計思路與分析:(1)創建新文件)創建新文件(2)將指

9、定內容寫進文件)將指定內容寫進文件(3)讀取文件內容)讀取文件內容(4)關閉文件)關閉文件6.2 進程與進程控制進程與進程控制6.2.1 進程進程1、什么是進程、什么是進程 進程是一個具有獨立功能的程序的一次動態執行進程是一個具有獨立功能的程序的一次動態執行過程。簡言之,進程就正在執行的程序。例如,過程。簡言之,進程就正在執行的程序。例如,打開一個打開一個Windows資源管理器是在執行一個進程,資源管理器是在執行一個進程,運行一個瀏覽器閱讀運行一個瀏覽器閱讀WEB網頁也是在執行一個進網頁也是在執行一個進程等。程等。 進程與程序是兩個不同的概念,程序是永進程與程序是兩個不同的概念,程序是永久的

10、、靜態的,進程是暫時的、動態的。久的、靜態的,進程是暫時的、動態的。進程的組成包括程序、數據和進程控制塊進程的組成包括程序、數據和進程控制塊(即進程狀態信息)。一個程序可以多次(即進程狀態信息)。一個程序可以多次加載到內存,成為同時運行的多個進程。加載到內存,成為同時運行的多個進程。2、進程的標識、進程的標識 進程的標識為進程號進程的標識為進程號PID。PID唯一的標識了一個唯一的標識了一個進程。進程。 PID是一個非零的正整數。通過調用函數是一個非零的正整數。通過調用函數getpid( )可以獲得當前進程的可以獲得當前進程的PID。函數。函數getpid( )的原型為:的原型為: pid_t

11、 getpid(void);其返回值為當前進程的進程號其返回值為當前進程的進程號PID。(返回值的數據類型(返回值的數據類型pid_t 是一個宏定義,是在是一個宏定義,是在#include中定義的中定義的int類型)。類型)。【例例6-4】 獲取當前進程的進程號獲取當前進程的進程號PID。 1 #include 2 #include 3 int main()4 5 printf(“PID = %d n”, getpid();6 return 0;7 將程序保存為將程序保存為proID.c,使用交叉編譯命令,使用交叉編譯命令arm-linux-gcc進行交叉編譯:進行交叉編譯:rootlocal

12、host test# arm-linux-gcc -o proID proID.c將其下載到嵌入式系統開發板上運行該程序:將其下載到嵌入式系統開發板上運行該程序:rootlocalhost ex5# ./proIDPID = 3061 注意,每次執行結果都不一定相同。注意,每次執行結果都不一定相同。3、創建進程、創建進程創建新進程的函數為創建新進程的函數為fork( ),其函數原型為:,其函數原型為:pid_t fork(void);fork調用失敗則返回調用失敗則返回-1,調用成功的返回值為進程,調用成功的返回值為進程號。號。fork函數被調用后會返回兩次。一次是在父進程中函數被調用后會返回

13、兩次。一次是在父進程中返回,另一次是在子進程中返回,這兩次的返回值返回,另一次是在子進程中返回,這兩次的返回值是不一樣的。在子進程中返回是不一樣的。在子進程中返回0值,而在父進程中值,而在父進程中返回子進程的進程號返回子進程的進程號PID。在執行在執行fork()之后,同一進程有兩個拷貝都在運行,之后,同一進程有兩個拷貝都在運行,也就是說,子進程具有與父進程相同的可執行程序也就是說,子進程具有與父進程相同的可執行程序和數據(簡稱映像)。和數據(簡稱映像)。【例例6-5】創建一個進程。創建一個進程。1 #include 2 #include 3 #include 4 int main()5 6

14、pid_t pid;7 pid = fork( );8 printf(“PID = %d n”, pid);9 return 0;10 將程序保存為將程序保存為process.c,使用交叉編譯命令,使用交叉編譯命令arm-linux-gcc進行交叉編譯:進行交叉編譯:rootlocalhost test# arm-linux-gcc -o process process.c將其下載到嵌入式系統開發板上運行該程序:將其下載到嵌入式系統開發板上運行該程序:rootlocalhost ex5# ./processPID = 0PID = 4138從上述運行結果中可以看出從上述運行結果中可以看出for

15、k函數的特點,概括函數的特點,概括起來就是起來就是“調用一次,返回兩次調用一次,返回兩次”,在父進程中調,在父進程中調用一次,在父進程和子進程中各返回一次。第一次用一次,在父進程和子進程中各返回一次。第一次的返回值的返回值PID = 0是在子進程中返回的進程號,第是在子進程中返回的進程號,第二次的返回值二次的返回值PID = 4138是在父進程中返回子進程是在父進程中返回子進程的進程號。的進程號。6.2.2 進程控制進程控制1、調用、調用exec函數運行執行程序函數運行執行程序 為了讓子進程能運行另外的執行程序,需要用到為了讓子進程能運行另外的執行程序,需要用到exec函數。當進程調用一種函數

16、。當進程調用一種exec函數時,該進程函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新的用戶空間代碼和數據完全被新程序替換,從新程序的啟動例程開始執行。程序的啟動例程開始執行。 exec函數提供了一個在進程中啟動另一個程序執函數提供了一個在進程中啟動另一個程序執行的方法。它可以根據指定的文件名或目錄名找行的方法。它可以根據指定的文件名或目錄名找到可執行文件,并用它取代原調用進程的數據段、到可執行文件,并用它取代原調用進程的數據段、代碼段和堆棧段,在執行完后,原調用進程的內代碼段和堆棧段,在執行完后,原調用進程的內容除了進程號外,其他全部被新的進程替換了。容除了進程號外,其他全部被新的進

17、程替換了。也就是說,調用也就是說,調用exec并不創建新進程,所以調用并不創建新進程,所以調用exec前后該進程的進程號前后該進程的進程號PID并未改變。并未改變。2、exec函數族函數族在嵌入式在嵌入式Linux中并沒有中并沒有exec函數,而是有六種以函數,而是有六種以exec開頭的函數,統稱開頭的函數,統稱exec函數:函數:#include int execl(const char *path, const char *arg, .);int execlp(const char *file, const char *arg, .);int execle(const char *path

18、, const char *arg, ., char *const envp);int execv(const char *path, char *const argv);int execvp(const char *file, char *const argv);int execve(const char *path, char *const argv, char *const envp);【例例6-6】應用應用execlp函數列出當前目錄下的所有文件。函數列出當前目錄下的所有文件。1 #include 2 #include 3 #include 4 int main()5 6 if (fo

19、rk() = 0)7 8 int ret;9 /*調用調用execlp()函數,這里相當于調用了函數,這里相當于調用了ls -l命令命令*/10 ret = execlp(ls, ls, -l, NULL);11 12 return 0;13 在該程序中,首先使用在該程序中,首先使用fork()函數創建一個子進程,函數創建一個子進程,然后在子進程里使用然后在子進程里使用execlp()函數。從程序中可函數。從程序中可以看到,這里的參數列表列出了在以看到,這里的參數列表列出了在shell中使用的中使用的命令名和選項。命令名和選項。將文件保存為將文件保存為execlp.c,使用交叉編譯命令,使用交

20、叉編譯命令arm-linux-gcc進行交叉編譯:進行交叉編譯:rootlocalhost test# arm-linux-gcc -o execlp execlp.c將其下載到嵌入式系統開發板上運行該程序,運行將其下載到嵌入式系統開發板上運行該程序,運行結果如下所示:結果如下所示:rootlocalhost test# ./execlpexeclpexeclp.cproidproid.c3、System函數函數system函數可以在一個程序的內部啟動另一函數可以在一個程序的內部啟動另一個程序,從而創建一個新進程。個程序,從而創建一個新進程。system函函數所需要的頭文件為:數所需要的頭文件

21、為: #include 其函數原型為:其函數原型為: int system(const char *string);4、進程調用的應用示例進程調用的應用示例【例例6-8】設已有設已有C語言的源程序語言的源程序Hello.c,現,現編寫一個程序,在程序中自動編譯編寫一個程序,在程序中自動編譯Hello.c,并運行編譯后的執行文件并運行編譯后的執行文件Hello。1 #include 2 #include 3 #include 4 int main()5 /* 調用調用system函數編譯命令函數編譯命令 */6 system(gcc -o Hello Hello.c);7 printf(Hell

22、o.c Compile successfully! n); 8 if(fork( )=0)9 10 /* 調用調用execlp函數執行運行程序命令函數執行運行程序命令 */ 11 if( execlp(./Hello, Hello, NULL) 0) 12 13 printf(execlp error n);14 15 16 sleep(1); /* 延時延時1秒,以等待秒,以等待Hello的輸出結果的輸出結果 */17 printf(Hello run successfully! n); 18 將程序保存為將程序保存為comple.c,使用編譯命令,使用編譯命令gcc進行編進行編譯程序:譯程

23、序:rootlocalhost test#gcc -o comple comple.c將已經編寫好的源程序將已經編寫好的源程序Hello.c保存在同一目錄下,保存在同一目錄下,運行運行comple程序,運行結果如下:程序,運行結果如下:rootlocalhost test# ./compleHello.c Compile successfully!Hello!Hello run successfully!Hello.c編譯、執編譯、執行后輸出的結果行后輸出的結果【例例6-9】編寫計算編寫計算1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 的程的程序,將其保存到文件

24、序,將其保存到文件sum.c中,然后自動編譯并運行該程序。中,然后自動編譯并運行該程序。 1 #include 2 #include 3 #include 4 #include 5 #include 6 int main()7 8 int fd;9 int len, size_r, size_w;10 char *buf = #include n int main()nnint i,s=0;n for(i=1; i=10; i+)n n s=s+i;n printf(%d=%d+%dn,s,s-i,i);n n n;11 fd=open(sum.c, O_CREAT|O_TRUNC|O_RDW

25、R, 0600);12 len = strlen(buf);13 size_w = write(fd, buf, len);14 將將buf中的程序代碼中的程序代碼寫入寫入sum.c文件文件15 char *buf_r500;16 lseek(fd,0,SEEK_SET);17 size_r = read(fd, buf_r,size_w); /察看察看sum.c文件內容文件內容18 printf(size_r = %d n, size_r);19 printf(Read: n %s n, buf_r);20 close(fd);21 printf(Write and Read OK! nn)

26、;22/編譯編譯sum.c程序程序23 system(gcc -o sum sum.c); 24 printf(sum.c compiled complete. n); 25 if(fork() = 0)26 /執行編譯后的執行編譯后的sum程序程序27if(execlp(./sum, ./sum, NULL) 0)28 29 printf(execlp error n);30 31 32 sleep(1);33 printf(sum run complete. n); 34 6.3 進程間通信進程間通信6.3.1 進程間的通信方式進程間的通信方式圖圖6.1 進程間通信進程間通信每個進程各自有

27、不同的用戶地址空間,任何一個進程的全每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數局變量在另一個進程中都看不到,所以進程之間要交換數據必須通過內核,在內核中開辟一塊緩沖區,進程據必須通過內核,在內核中開辟一塊緩沖區,進程1把數把數據從用戶空間拷到內核緩沖區,進程據從用戶空間拷到內核緩沖區,進程2再從內核緩沖區把再從內核緩沖區把數據讀走,如圖數據讀走,如圖6.1所示。所示。 使用較多的進程間通信方式主要有以下幾種。使用較多的進程間通信方式主要有以下幾種。(1)管道()管道(Pipe)及有名管道()及有名管道(namedpipe):管道可用于具

28、有親):管道可用于具有親緣關系進程間的通信,有名管道,除具有管道所具有的功能外,它還緣關系進程間的通信,有名管道,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信。允許無親緣關系進程間的通信。(2)共享內存()共享內存(Sharedmemory):可以說這是最有用的進程間通):可以說這是最有用的進程間通信方式。它使得多個進程可以訪問同一塊內存空間,不同進程可以及信方式。它使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程中對共享內存中數據的更新。這種通信方式需要依靠時看到對方進程中對共享內存中數據的更新。這種通信方式需要依靠某種同步機制,如互斥鎖和信號量等。某種同步機制,

29、如互斥鎖和信號量等。(3)消息隊列()消息隊列(MessgeQueue):消息隊列是消息的鏈接表,包括):消息隊列是消息的鏈接表,包括Posix消息隊列消息隊列SystemV消息隊列。它克服了前兩種通信方式中信息消息隊列。它克服了前兩種通信方式中信息量有限的缺點,具有寫權限的進程可以按照一定的規則向消息隊列中量有限的缺點,具有寫權限的進程可以按照一定的規則向消息隊列中添加新消息;對消息隊列有讀權限的進程則可以從消息隊列中讀取消添加新消息;對消息隊列有讀權限的進程則可以從消息隊列中讀取消息。息。(4)信號()信號(Signal):信號是在軟件層次上對中斷機制的一種模擬,):信號是在軟件層次上對中

30、斷機制的一種模擬,它是比較復雜的通信方式,用于通知進程有某事件發生,一個進程收它是比較復雜的通信方式,用于通知進程有某事件發生,一個進程收到一個信號與處理器收到一個中斷請求效果上可以說是一樣的。到一個信號與處理器收到一個中斷請求效果上可以說是一樣的。(5)信號量()信號量(Semaphore):主要作為進程之間以及同一進程的不同):主要作為進程之間以及同一進程的不同線程之間的同步和互斥手段。線程之間的同步和互斥手段。(6)套接字()套接字(Socket):這是一種更為一般的進程間通信機制,它可):這是一種更為一般的進程間通信機制,它可用于網絡中不同機器之間的進程間通信,應用非常廣泛。用于網絡中

31、不同機器之間的進程間通信,應用非常廣泛。6.3.2 管道管道管道是一種最基本的進程機制,它由管道是一種最基本的進程機制,它由pipe函數創建:函數創建:#include int pipe(int fd2);其中,數組其中,數組fd2的元素為管道的兩個文件描述符,的元素為管道的兩個文件描述符,管道創建之后就可以直接操作這兩個文件描述符。管道創建之后就可以直接操作這兩個文件描述符。pipe函數調用成功返回函數調用成功返回0,調用失敗返回,調用失敗返回-1。管道通信的思想管道通信的思想 : 調用調用pipe函數時在內核中開辟一塊緩沖函數時在內核中開辟一塊緩沖區(稱為管道)用于通信,它有一個寫入區(稱

32、為管道)用于通信,它有一個寫入端一個讀出端。發送進程可以源源不斷的端一個讀出端。發送進程可以源源不斷的從從pipe一端寫入數據流,在規定的一端寫入數據流,在規定的pipe文文件的最大長度(如件的最大長度(如4096字節)范圍內,每字節)范圍內,每次寫入的信息長度是可變的。接收進程在次寫入的信息長度是可變的。接收進程在需要時可以從需要時可以從pipe的另一端讀出數據,讀的另一端讀出數據,讀出單位長度也是可變的。出單位長度也是可變的。 管道操作的管道操作的寫入與讀取寫入與讀取函數:函數:write(fd1, buf, size);功能功能:把:把buf中的長度為中的長度為size字符的消息送入管道

33、入口字符的消息送入管道入口fd1fd1pipe入口入口 buf:存放消息的空間存放消息的空間size :要寫入的字符長度要寫入的字符長度 read(fd0, buf, size);fd0Pipe的出口的出口功能:從功能:從pipe出口出口fd0讀出讀出size字符的消息置入字符的消息置入 buf中。中。在用戶程序,在用戶程序,fd0指向管道的讀出端,指向管道的讀出端,fd1指向管道的寫指向管道的寫入端。所以管道在用戶程序看起來就像一個打開的文件,通入端。所以管道在用戶程序看起來就像一個打開的文件,通過過read(fd0);或者或者write(fd1);向這個文件讀寫數據其實是向這個文件讀寫數據

34、其實是在讀寫內核緩沖區。如圖在讀寫內核緩沖區。如圖6.2所示。所示。建立了管道之后如何實現兩個進程間的通信呢?可建立了管道之后如何實現兩個進程間的通信呢?可以按下面的步驟通信。以按下面的步驟通信。(1)父進程調用)父進程調用pipe開辟管道,得到兩個文件描開辟管道,得到兩個文件描述符指向管道的兩端。述符指向管道的兩端。(2)父進程調用)父進程調用fork創建子進程,那么子進程也有創建子進程,那么子進程也有兩個文件描述符指向同一管道。兩個文件描述符指向同一管道。(3)父進程關閉管道讀端,子進程關閉管道寫端。)父進程關閉管道讀端,子進程關閉管道寫端。父進程可以往管道里寫,子進程可以從管道里讀,父進

35、程可以往管道里寫,子進程可以從管道里讀,管道是用環形隊列實現的,數據從寫端流入從讀端管道是用環形隊列實現的,數據從寫端流入從讀端流出,這樣就實現了進程間通信。流出,這樣就實現了進程間通信。【例例6-10】編寫一個程序,建立一個管道編寫一個程序,建立一個管道pipe,同,同時父進程生成一個子進程,子進程向時父進程生成一個子進程,子進程向pipe中寫入一中寫入一個字符串,父進程從個字符串,父進程從pipe中讀取該字符串。中讀取該字符串。1 #include 2 #include 3 int main()4 5 int x, fd2;6 char buf30, s30;7 pipe(fd); 8 x

36、 = fork(); 9 if(x = 0)10 11 sprintf(buf,This is an pipe!);12 write(fd1, buf, 30); 13 exit(0);14 15 else16 17 wait(0); 18 read(fd0, s, 30); 19 printf(read: %s n, s);20 21 創建管道創建管道子進程子進程對字符數組對字符數組buf賦值賦值將字符數組將字符數組buf的字的字符串寫入管道符串寫入管道父進程等待子父進程等待子進程終止進程終止父進程從管道讀取父進程從管道讀取數據存放到數組數據存放到數組s6.3.3 共享內存共享內存所謂共享內

37、存,就是在內核空間開辟一塊內所謂共享內存,就是在內核空間開辟一塊內存區域,用于多個進程之間進行交換信息,存區域,用于多個進程之間進行交換信息,把這塊內核空間的內存區域稱為共享內存區。把這塊內核空間的內存區域稱為共享內存區。 圖6.3 共享內存實現共享內存的步驟實現共享內存的步驟 : 第一步是在內核空間創建共享內存,也就是從內第一步是在內核空間創建共享內存,也就是從內存中獲得一塊共享內存區域。這里需要使用函數存中獲得一塊共享內存區域。這里需要使用函數shmget()來創建共享內存;來創建共享內存; 第二步是在進程的地址空間映射共享內存,也就第二步是在進程的地址空間映射共享內存,也就是把這塊創建的

38、共享內存區域映射到進程空間中。是把這塊創建的共享內存區域映射到進程空間中。這里需要使用的函數這里需要使用的函數shmat()來映射共享內存。來映射共享內存。 共享內存的函數共享內存的函數 函函 數數功功 能能 說說 明明int shmget(key_t key, int size, int shmget(key_t key, int size, int flag);int flag);創建由參數創建由參數sizesize指定大小的共享指定大小的共享內存內存char char * *shmat(int shm_id, const shmat(int shm_id, const void void

39、 * *addr, int flag);addr, int flag);將共享內存映射到調用進程空間將共享內存映射到調用進程空間中的指定地址中的指定地址int shmctl(int shm_id, int cmd, int shmctl(int shm_id, int cmd, struct shmid_ds struct shmid_ds * *buf);buf);對共享內存進行操作對共享內存進行操作int shmdt(const void int shmdt(const void * *addr);addr);解除當前進程與共享內存的映射解除當前進程與共享內存的映射調用的共享內存函數所需頭

40、文件為:調用的共享內存函數所需頭文件為: #include 1、創建共享內存區域的、創建共享內存區域的shmget()函數函數shmget()函數用于創建共享內存區域。其函數原型為:函數用于創建共享內存區域。其函數原型為: int shmget(key_t key, int size, int flag);函數返回值:若創建共享內存成功則返回共享內存標函數返回值:若創建共享內存成功則返回共享內存標識符;若出錯,則為識符;若出錯,則為 -1。創建共享內存創建共享內存shmget()函數的參數說明如下:函數的參數說明如下:(1)key參數:共享內存的鍵值,多個進程可以通過參數:共享內存的鍵值,多個

41、進程可以通過它訪問同一個共享內存。常用一個特殊值它訪問同一個共享內存。常用一個特殊值IPC_PRIVATE,創建當前進程的私有共享內存。,創建當前進程的私有共享內存。(2)size參數:指定創建共享內存的大小。參數:指定創建共享內存的大小。(3)flag參數:操作權限,詳見第參數:操作權限,詳見第3章表章表3.2。【例例6-11】創建一個共享內存區域。創建一個共享內存區域。1 #include 2 #include 3 #include 4 int main()5 int shm_id;6 shm_id = shmget(IPC_PRIVATE, 4096, 0666);7 if(shm_id

42、 0)8 9 perror(shmget id 0 );10 exit(0); 11 12 printf(成功建立共享內存區域成功建立共享內存區域: %d n, shm_id);13 system(ipcs -m);14 創建共享內存區域創建共享內存區域顯示共享內存標識符顯示共享內存標識符顯示當前共享顯示當前共享內存狀況內存狀況2、建立進程空間到共享內存映射的、建立進程空間到共享內存映射的shmat()函數函數shmat( )函數用于建立進程空間地址到共享內存的映射。其函數用于建立進程空間地址到共享內存的映射。其函數原型為:函數原型為:char *shmat(int shm_id, const

43、 void *addr, int flag);函數返回值:若映射共享內存成功則返回進程空間中被映射函數返回值:若映射共享內存成功則返回進程空間中被映射的區域地址;若出錯,則為的區域地址;若出錯,則為 -1。建立共享內存映射建立共享內存映射shmgat()函數的參數說明如下:函數的參數說明如下:(1)shm_id參數:要映射的共享內存標識符。參數:要映射的共享內存標識符。(2)addr參數:指定在調用進程中映射共享內存的地址。參數:指定在調用進程中映射共享內存的地址。通常取值為通常取值為0,表示由系統自動分配地址。,表示由系統自動分配地址。(3)flag參數:設置共享內存的操作權限。若取值為參數

44、:設置共享內存的操作權限。若取值為0,表,表示可對共享內存進行讀寫操作。示可對共享內存進行讀寫操作。【例例6-12】建立一個映射到例建立一個映射到例6-11所建共享內存的所建共享內存的進程,并向共享內存寫入數據。進程,并向共享內存寫入數據。1 #include 2 #include 3 #include 4 int main(int argc, char *argv)5 int shm_id; 6 char *shm_buf;7 shm_id = atoi(argv1); 8 shm_buf=shmat(shm_id, 0, 0); 9 printf(寫入數據到共享內存:寫入數據到共享內存:

45、n);9 sprintf (shm_buf, 對共享內存讀寫操作對共享內存讀寫操作);10 printf(%s n, shm_buf); 11 獲取要建立映射獲取要建立映射的共享內存(由的共享內存(由命令行輸入)命令行輸入)通過映射區寫入通過映射區寫入數據到共享內存數據到共享內存返回映射區的地返回映射區的地址址【例例6-13】建立一個從共享內存中讀取數據的進程。建立一個從共享內存中讀取數據的進程。1 #include 2 #include 3 int main(int argc, char *argv)4 5 int shm_id;6 char *shm_buf, str;7 shm_id =

46、 atoi(argv1);8 shm_buf=shmat(shm_id, 0, 0);9 printf(讀取共享內存數據:讀取共享內存數據: n);10 sprintf(str, shm_buf);10 printf(%s n, str);11 system(ipcs -m);12 通過映射區讀取共通過映射區讀取共享內存的數據到字享內存的數據到字符數組符數組str3、對共享內存進行操作函數、對共享內存進行操作函數進程與共享內存建立了映射之后,就可以在進程中進程與共享內存建立了映射之后,就可以在進程中對共享內存進行操作,其操作函數的原型如下:對共享內存進行操作,其操作函數的原型如下: int s

47、hmctl(int shm_id, int cmd, struct shmid_ds *buf);對共享內存操作對共享內存操作shmctl()函數的參數說明如下:函數的參數說明如下:(1)shm_id參數:所映射的共享內存標識符。參數:所映射的共享內存標識符。(2)cmd參數:指定所要進行的操作。參數:指定所要進行的操作。cmd的可的可以取值以取值IPC_STAT、IPC_SET、IPC_RMID、SHM_LOCK、SHM_UNLOCK。(3)buf參數:結構體型指針參數:結構體型指針 。4、解除進程到共享內存映射的、解除進程到共享內存映射的shmdt()函數函數 shmdt( )函數為解除進

48、程到共享內存的映函數為解除進程到共享內存的映射,其函數原型為:射,其函數原型為:int shmdt(const void *addr); 函數參數函數參數addr為被映射的共享內存區域的為被映射的共享內存區域的地址。地址。 函數返回值:若解除映射成功則返回函數返回值:若解除映射成功則返回0;若;若出錯,則為出錯,則為 -1。【例例6-14】解除一個進程到共享內存的映射,并釋解除一個進程到共享內存的映射,并釋放內存空間。放內存空間。1 #include 2 #include 3 int main(int argc, char *argv)4 5 int shm_id;6 char *shm_bu

49、f;7 shm_id = atoi(argv1);8 shm_buf=shmat(shm_id, 0, 0);9 shmdt(shm_buf);10 shmctl(shm_id, IPC_RMID, NULL);11 system(ipcs -m);12 定位映射地址定位映射地址解除進程到共享解除進程到共享內存的映射內存的映射釋放共享內存空間釋放共享內存空間6.4 嵌入式嵌入式Linux串口通信技術串口通信技術 6.4.1 嵌入式嵌入式Linux串口通信基礎串口通信基礎1、串口通信的工作原理、串口通信的工作原理 所謂所謂“串口通信串口通信”是指外設和計算機間使是指外設和計算機間使用一根數據信號

50、線用一根數據信號線(另外需要地線另外需要地線),數據在數據在一根數據信號線上一位一位地進行傳輸,一根數據信號線上一位一位地進行傳輸,每一位數據都占據一個固定的時間長度。每一位數據都占據一個固定的時間長度。 在串口傳輸中,發送方為了告訴接收方,在串口傳輸中,發送方為了告訴接收方,新的數據字節分組到達,在每一個數據字新的數據字節分組到達,在每一個數據字節分組前面有一個起始位(通常是節分組前面有一個起始位(通常是0),為),為了讓接收方知道字節已經結束,在每一個了讓接收方知道字節已經結束,在每一個數據字節分組后面有一個停止位(通常是數據字節分組后面有一個停止位(通常是1)。接收方一旦檢測到停止位,接

51、收方會)。接收方一旦檢測到停止位,接收方會一直等待,直到下一個開始位。一直等待,直到下一個開始位。 圖圖6.4 串行傳輸數據的工作原理串行傳輸數據的工作原理嵌入式系統串行通信采用嵌入式系統串行通信采用EIA RS-232C標準,標準,為單向不平衡傳輸方式,信號電平標準為單向不平衡傳輸方式,信號電平標準12V,負邏輯,即邏輯負邏輯,即邏輯1(MARKING)表示為信號電平表示為信號電平-12V,邏輯,邏輯0(SPACING)表示為信號電平表示為信號電平+12V,最大傳送距離,最大傳送距離15米,最大傳送速率米,最大傳送速率19.6K波特,平時線路保持為波特,平時線路保持為1,傳送數據開,傳送數據

52、開始時,先發送起始位始時,先發送起始位(其數據值是其數據值是0),然后傳然后傳8(或或7,6,5)個數據位個數據位(其數據值是其數據值是0,1),接,接著可傳著可傳1位奇偶校驗位,最后為位奇偶校驗位,最后為12個停止位個停止位(其數據值是其數據值是1),由此可見,傳送一個,由此可見,傳送一個ASCII字字符符(7位位),加上同步信號最少需,加上同步信號最少需9位數據位。位數據位。 2、常用信號引腳與串口通信接線、常用信號引腳與串口通信接線 圖圖6.5 串口的連接線串口的連接線6.4.2 嵌入式嵌入式Linux串口設置詳解串口設置詳解1、起始位、起始位 通信線路上沒有數據被傳送時,處于邏輯通信線

53、路上沒有數據被傳送時,處于邏輯“1”的狀態。當發送設備要發送字符數據時,首的狀態。當發送設備要發送字符數據時,首先發送一個邏輯先發送一個邏輯“0”信號,這個邏輯低電平信號,這個邏輯低電平就是起始位。起始位通過通信線路傳輸到接就是起始位。起始位通過通信線路傳輸到接收端,接收端檢測到這個低電平后,就開始收端,接收端檢測到這個低電平后,就開始準備接收數據位信號。起始位所起的作用就準備接收數據位信號。起始位所起的作用就是使通信雙方同步。是使通信雙方同步。 2、數據位、數據位 當接收端收到起始位后,開始接收數據位。當接收端收到起始位后,開始接收數據位。數據位的個數可以是數據位的個數可以是58位。在數據傳

54、送過位。在數據傳送過程中,數據位從最低有效位開始傳送,接收程中,數據位從最低有效位開始傳送,接收端收到數據后,依次將其轉換成并行數據。端收到數據后,依次將其轉換成并行數據。 3、奇偶校驗位、奇偶校驗位數據位發送完后,為了保證數據的可靠性,數據位發送完后,為了保證數據的可靠性,還要再傳送一個奇偶校驗位。奇偶校驗用于還要再傳送一個奇偶校驗位。奇偶校驗用于差錯檢測。如果選擇偶校驗,則數據位和奇差錯檢測。如果選擇偶校驗,則數據位和奇偶位的邏輯偶位的邏輯“1”的個數必須為偶數,相反,的個數必須為偶數,相反,如果是奇校驗,數據位和奇偶位的邏輯如果是奇校驗,數據位和奇偶位的邏輯“1”的個數為奇數。的個數為奇

55、數。4、停止位、停止位在奇偶位或數據位(當無奇偶校驗時)之后在奇偶位或數據位(當無奇偶校驗時)之后發送停止位。停止位表示一個數據的結束。發送停止位。停止位表示一個數據的結束。它可以是它可以是12位的低電平。接收端收到停止位的低電平。接收端收到停止位后,通信線路便恢復邏輯位后,通信線路便恢復邏輯“1”的狀態,直的狀態,直到下一個數據的起始位到來。到下一個數據的起始位到來。5、波特率設置、波特率設置 通信線路上傳輸的位(碼元)信號都必須保通信線路上傳輸的位(碼元)信號都必須保持一致的信號持續時間,單位時間內傳送碼持一致的信號持續時間,單位時間內傳送碼元的數目稱為波特率。元的數目稱為波特率。 對于大

56、多數嵌入式設備來說,其波特率都設對于大多數嵌入式設備來說,其波特率都設置為置為115200。 在在Linux中,所有的設備文件一般都位于中,所有的設備文件一般都位于“/dev”下,其中串口一、串口二所對應的下,其中串口一、串口二所對應的設備名依次為設備名依次為“/dev/ttyS0”、“/dev/ttyS1”,可以查看在可以查看在“/dev”下的文件以確認。下的文件以確認。 6.4.3RS232C標準標準 1、電氣特性、電氣特性 2、引腳定義、引腳定義3、字符(幀)格式、字符(幀)格式 RS232C采用起止式異步通信協議,其特點是一個采用起止式異步通信協議,其特點是一個字符接著一個字符進行傳輸

57、,并且傳送一個字符總字符接著一個字符進行傳輸,并且傳送一個字符總是以起始位開始,以停止位結束,字符之間沒有固是以起始位開始,以停止位結束,字符之間沒有固定的時間間隔要求。其傳輸格式如圖定的時間間隔要求。其傳輸格式如圖6.1所示,每一所示,每一個字符的前面都有一位起始位(低電平,邏輯值個字符的前面都有一位起始位(低電平,邏輯值“0”),字符本身有),字符本身有58位數據位,接著字符后面位數據位,接著字符后面是一位校驗碼(也可以沒有校驗碼),最后是停止是一位校驗碼(也可以沒有校驗碼),最后是停止位。停止位后面是不定長度的空閑位。停止位和空位。停止位后面是不定長度的空閑位。停止位和空閑位都規定為高電

58、平(邏輯值閑位都規定為高電平(邏輯值“1”),這樣就能保),這樣就能保證起始位開始處一定有一個下跳沿,便于接收方識證起始位開始處一定有一個下跳沿,便于接收方識別。別。4、握手協議、握手協議 (1)DTR和和DSR握手情況握手情況5、雙機互連方式、雙機互連方式 (2)RTS和和CTS握手情況握手情況5、雙機互連方式、雙機互連方式 5、雙機互連方式、雙機互連方式 (3)無硬件握手情況)無硬件握手情況a. 串口簡介串口簡介串行口是計算機一種常用的接口,具有連接線少,通訊簡單,得到廣泛的使用。常用的串口是 RS-232-C 接口,它是在 1970 年由美國電子工業協會(EIA)聯合貝爾系統、 調制解調

59、器廠家及計算機終端生產廠家共同制定的用于串行通訊的標準。它的全名是“數據終端設備(DTE)和數據通訊設備(DCE)之間串行二進制數據交換接口技術標準”該標準規定采用一個 25 個腳的 DB25 連接器,對連接器的每個引腳的信號內容加以規定,還對各種信號的電平加以規定。傳輸距離在碼元畸變小于 4% 的情況下,傳輸電纜長度應為 50 英尺。Linux 操作系統從一開始就對串行口提供了很好的支持,本文就 Linux 下的串行口通訊編程進行簡單的介紹,如果要非常深入了解,建議看看本文所參考的Serial Programming Guide for POSIX Operating Systems。序號信

60、號名稱符號流向功能2發送數據TXDDTEDCEDTE發送串行數據3接收數據RXDDTEDCEDTE 接收串行數據4請求發送RTSDTEDCEDTE 請求 DCE 將線路切換到發送方式5允許發送CTSDTEDCEDCE 告訴 DTE 線路已接通可以發送數據6數據設備準備好DSRDTEDCEDCE 準備好7信號地信號公共地8載波檢測DCDDTEDCE表示 DCE 接收到遠程載波20數據終端準備好DTRDTEDCEDTE 準備好22振鈴指示RIDTEDCE表示 DCE 與線路接通,出現振鈴計算機串口的引腳說明計算機串口的引腳說明6.4.4串口驅動程序的編寫串口驅動程序的編寫 一個串口驅動程序,通常包

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論