第6章linux
這章講進程、虛擬內存和環境變量等。程序員
進程是一個可執行程序的實例。一個程序能夠建立不少進程。web
進程是由內核定義的抽象實體,內核爲此實體分配執行程序所需的系統資源。面試
從內核的角度來看,進程是由用戶內存空間和內核數據結構組成的。程序的代碼和代碼中的變量存放在用戶內存空間,內核數據結構用於維護進程狀態信息。編程
對於每一個進程都有一個惟一的進程號(進程ID)(正數),用來標識系統中的某個程序。vim
getpid(),返回調用該函數的進程的進程ID。數組
1 #include <unistd.h> 2 3 pid_t getpid(void);
老是成功調用並返回調用該函數的進程的進程ID。bash
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
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 }
結果以下:
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 }
結果:
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:吐槽。。。。。。還要繼續努力,努力成爲一個後臺開發。。。。。。。。。