《Linux/Unix系統編程手冊》讀書筆記3


《Linux/Unix系統編程手冊》讀書筆記 目錄

第6章

這章講進程、虛擬內存和環境變量等。

進程是一個可執行程序的實例。一個程序可以創建很多進程。

進程是由內核定義的抽象實體,內核為此實體分配執行程序所需的系統資源。

從內核的角度來看,進程是由用戶內存空間和內核數據結構組成的。程序的代碼和代碼中的變量存放在用戶內存空間,內核數據結構用於維護進程狀態信息。

對於每個進程都有一個唯一的進程號(進程ID)(正數),用來標識系統中的某個程序。

 

getpid(),返回調用該函數的進程的進程ID。

1 #include <unistd.h>
2 
3 pid_t getpid(void);

總是成功調用並返回調用該函數的進程的進程ID。

getppid(),返回自己的父進程的進程ID。

1 #include <unistd.h>
2 
3 pid_t getppid(void);

總是成功調用並返回該函數的父進程的ID。

PS:所有進程的始祖為init進程(進程ID為1)。

 

內存布局:

Linux/x86-32進程內存結構如下:

文本段存放着程序運行的機器語言指令,具有只讀屬性,防止進程通過錯誤指針修改自身指令

初始化數據段存在着顯式初始化的全局變量和靜態變量

未初始化數據段存放着未進行顯式初始化的全局變量和靜態變量。將已經初始化的全局變量和靜態變量與未進行初始化的全局變量和靜態變量分開存放是因為程序在存放在磁盤上沒有必要為未初始化的變量分配存儲空間,只需要記錄未初始化數據段的位置和大小,在程序運行的時候再分配空間

堆(heap)是在運行時為變量動態進行內存分配的區域,堆頂叫program break,后面一章就是通過brk()和sbrk()來調整堆的大小進而來分配內存(malloc()就是基於這兩個函數實現)。

棧(stack)由棧幀組成的,可以動態增長和收縮的段,系統為每個當前調用的函數分配一個棧幀,里面存儲了函數的局部變量(自動變量)。

通過虛擬內存管理技術可以高效的使用CPU和RAM資源。因為大多數都有兩個局部性:空間局部性和時間局部性

虛擬內存將每個程序使用的內存分割成小塊、固定大小的頁單元。對於RAM,將其划分成一系列與虛擬內存頁面大小相同的頁幀。在程序運行的時候,只有一部分的頁駐留在物理內存頁幀中,當進程需要訪問的頁面不在物理內存中,就會發生頁面錯誤,並掛起進程的執行,再將頁面從磁盤載入到物理內存。

內核為每個進程維護一張頁表,通過頁表可以虛擬地址對應的物理地址。

虛擬內存的實現需要硬件有分頁內存管理單元(將虛擬內存地址轉成相應的物理內存地址)。

通過虛擬內存管理可以將虛擬地址空間與RAM物理地址空間隔開:使進程與進程、進程與內核相互隔離,可以保護進程和內核的內存;可以使得多個進程共享內存;實現內存保護機制;程序員和一些程序無需關注程序在物理內存的布局;還可以提高CPU利用率。

 

命令行參數:

int argc; 命令行參數個數;

char *argv[];指向命令行參數的指針數組;

PS:這兩個參數是main()函數的局部變量,所以其他函數要使用這兩個參數需要進行傳遞或者設置一個指向argv的全局變量。

 

環境列表:

每個進程都有與自己相關的環境列表。

每條記錄的形式為name=value;

新進程創建時會繼承父進程的環境副本(原始的進程間通信方式)。

可以通過printenv輸出當前的環境列表:

 1 lancelot@debian:~$ printenv
 2 SSH_AGENT_PID=4370
 3 CLUTTER_IM_MODULE=ibus
 4 GPG_AGENT_INFO=/home/lancelot/.cache/keyring-A5CMa0/gpg:0:1
 5 TERM=xterm
 6 SHELL=/bin/bash
 7 XDG_SESSION_COOKIE=66aede8f90c2ace983c1e18451c8875a-1397554660.783480-1822251707
 8 HUSHLOGIN=FALSE
 9 GNOME_KEYRING_CONTROL=/home/lancelot/.cache/keyring-A5CMa0
10 LC_ALL=zh_CN.UTF-8
11 USER=lancelot
12 LIBGL_DRIVERS_PATH=/usr/lib/i386-linux-gnu/dri:/usr/lib/x86_64-linux-gnu/dri
13 LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:
14 DESKTOP_AUTOSTART_ID=10dc1ad7011f06891f139755466392940400000043790001
15 SSH_AUTH_SOCK=/home/lancelot/.cache/keyring-A5CMa0/ssh
16 SESSION_MANAGER=local/debian:@/tmp/.ICE-unix/4379,unix/debian:/tmp/.ICE-unix/4379
17 PATH=/home/lancelot/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/lancelot/bin
18 MAIL=/var/mail/lancelot
19 LC_COLLATE=C
20 PWD=/home/lancelot
21 XMODIFIERS=@im=ibus
22 LANG=zh_CN.UTF-8
23 HOME=/home/lancelot
24 SHLVL=2
25 LANGUAGE=zh_CN.UTF-8:zh:en_US:en
26 GNOME_DESKTOP_SESSION_ID=this-is-deprecated
27 LOGNAME=lancelot
28 XDG_DATA_DIRS=/usr/share/gnome:/usr/local/share/:/usr/share/
29 QT4_IM_MODULE=ibus
30 DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-q3rPwh3OMI,guid=5495fcf0de26e09259bc54ef534cfde4
31 WINDOWPATH=7
32 TEXTDOMAIN=im-config
33 DISPLAY=:0
34 GTK_IM_MODULE=ibus
35 TEXTDOMAINDIR=/usr/share/locale/
36 XAUTHORITY=/home/lancelot/.Xauthority
37 _=/usr/bin/printenv
View Code

 

getenv(),獲得進程環境的值

1 #include <stdlib.h>
2 
3 char *getenv(const char *name);

對於存在name的環境變量就返回對應的value的指針,如果不存在就返回NULL

putenv(),修改環境變量

1 #include <stdlib.h>
2 
3 int putenv(char *string);

將name=value形式的字符串添加到當前的環境列表。成功返回0,失敗返回非0值

PS:putenv是將environ變量的某個元素指向string指向的位置,並不是指向string指向字符串的副本。所以如果修改string指向的內容,環境變量也會改變,因此string指向后來不能是自動變量。

PS:當string里不包含等號的時候,將會在環境列表移除string命名的環境變量

setenv(),往環境添加變量

1 #include <stdlib.h>
2 
3 int setenv(const char *name, const char *value, int overwrite);

name,value與name=value對應,overwrite決定時候改變環境。如果以name標識的變量存在環境里而且overwrite為0,就不改變環境;否則如果overwrite不為0就總是改變環境。

成功返回0, 失敗返回-1

PS:不同與putenv,setenv是分配一塊內存緩沖區,將name和value指向的字符串復制到緩沖區里

unsetenv(),移除環境變量

1 #include <stdlib.h>
2 
3 int unsetenv(const char *name);

移除name標識的變量

成功調用返回0,失敗返回-1。

clear(),清除整個環境

1 #define _BSD_SOURCE
2 #include <stdlib.h>
3 
4 int clearenv(void);

成功調用返回0,失敗返回非0值。

PS:setenv和clearenv會導致內存泄露:setenv分配了一塊內存緩沖區,但是clearenv沒有釋放這個緩沖區。

 

-----------------省略setjmp()和longjump(),非局部跳轉(不同於goto,可以從一個函數跳轉到另一個函數)------------

自己比較少使用。。。。還有整整一本下冊啊。。。今天面試被虐了。。。

要繼續認真看這本書。。。后台開發不容易做啊。。。。。。。。。。。。

----------------中間吐槽完畢-------------------------------------------------------------------------------------------------------------

 

練習:

6-1:編譯程序清單6-1的程序,使用ls -l 命令顯示可執行文件的大小。雖然程序包含一個大約10MB的數組,但可執行文件大小遠小於此,為什么?

程序清單如下:

 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  mem_segment.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年03月19日 00時03分10秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  *
16  * =====================================================================================
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 
22 char globBuf[65536];
23 int primes[] = {2, 3, 5, 7};
24 
25 static int square(int x){
26     int result;
27 
28     result = x * x;
29     return result;
30 }
31 
32 static void doCal(int val){
33     printf("The square of %d is %d\n", val, square(val));
34 
35     if(val < 1000){
36         int t;
37 
38         t = val * val * val;
39         printf("The cube of %d is %d\n", val, t);
40     }
41 }
42 
43 int main(int argc, char *argv[]){
44     static int key = 9973;
45     static char mbuf[10240000];
46     char *p;
47 
48     p = (char *)malloc(1024);
49 
50     doCal(key);
51 
52     exit(EXIT_SUCCESS);
53 }
6-1

結果如下:

lancelot@debian:~/Code/tlpi$ gcc -o mem_segments mem_segment.c 
lancelot@debian:~/Code/tlpi$ gvim mem_segment.c 
lancelot@debian:~/Code/tlpi$ ls -l mem_segments
-rwxr-xr-x 1 lancelot lancelot 7500  4月 15 16:00 mem_segments

因為那個10MB的數組(靜態)沒有初始化,所以是存放在未初始化的數據,只有在運行的時候再為其分配內存。

 

6-3:使用getenv()函數,和putenv()函數,必要時可以直接修改environ,來實現setenv()函數和unsetenv()函數。(結合6-4程序清單)

 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  6-3.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年04月11日 15時30分32秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  *
16  * =====================================================================================
17  */
18 
19 #define _GNU_SOURCE
20 #include <stdlib.h>
21 #include <string.h>
22 #include "tlpi_hdr.h"
23 
24 extern char **environ;
25 char env[1000];
26 
27 int M_setenv(const char *name, const char *value, int overwrite){
28     strcpy(env, name);
29     strcat(env, "=");
30     strcat(env, value);
31 
32     if(getenv(name) != NULL && overwrite == 0)
33         return 0;
34     if(putenv(env) == 0)
35         return 0;
36     else
37         return -1;
38 }
39 
40 int M_unsetenv(const char *name){
41     if(putenv((char *)name) == 0)
42         return 0;
43     return -1;
44 }
45 
46 int main(int argc, char *argv[]){
47     int j;
48     char **ep;
49 
50     clearenv();
51 
52     for(j = 1; j < argc; j++)
53         if(putenv(argv[j]) != 0)
54             errExit("putenv: %s", argv[j]);
55 
56     if(M_setenv("GREET", "Hello world", 0) == -1)
57        errExit("M_setenv");
58 
59     M_unsetenv("BYE");
60 
61     for(ep = environ; *ep != NULL; ep++)
62         puts(*ep);
63 
64     exit(EXIT_SUCCESS);
65 }

6-4程序清單:

 1 /*
 2  * =====================================================================================
 3  *
 4  *       Filename:  modify_env.c
 5  *
 6  *    Description:  
 7  *
 8  *        Version:  1.0
 9  *        Created:  2014年03月19日 10時48分04秒
10  *       Revision:  none
11  *       Compiler:  gcc
12  *
13  *         Author:  alan (), alan19920626@gmail.com
14  *   Organization:  
15  *
16  * =====================================================================================
17  */
18 
19 #define _GNU_SOURCE
20 #include <stdlib.h>
21 #include "tlpi_hdr.h"
22 
23 extern char **environ;
24 
25 int main(int argc, char * argv[]){
26     int j;
27     char **ep;
28 
29     clearenv();
30 
31     for(j = 1; j < argc; j++)
32         if(putenv(argv[j]) != 0)
33             errExit("putenv: %s", argv[j]);
34 
35     if(setenv("GREET", "Hello world", 0) == -1)
36         errExit("setenv");
37     
38     unsetenv("BYE");
39 
40     for(ep = environ; *ep != NULL; ep++)
41         puts(*ep);
42 
43     exit(EXIT_SUCCESS);
44 }
View Code

結果:

lancelot@debian:~/Code/tlpi$ ./6-3 "GREET=Guten" SHELL=/bin/bash BYE=Ciao
GREET=Guten
SHELL=/bin/bash
lancelot@debian:~/Code/tlpi$ ./6-3 SHELL=/bin/sh BYE=byebye
SHELL=/bin/sh
GREET=Hello world

 

PS:吐槽。。。。。。還要繼續努力,努力成為一個后台開發。。。。。。。。。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM