0. 引言
在學(xué)習(xí)進(jìn)程控制前拯勉,先了解進(jìn)程運(yùn)行的環(huán)境蚁滋,如:main函數(shù)是如何被調(diào)用的;命令行參數(shù)是如何傳遞給新程序的焦匈;程序在內(nèi)存空間中是什么樣的結(jié)構(gòu)血公;進(jìn)程的終止方式等。
1. main函數(shù)
1.1 程序執(zhí)行的入口函數(shù)
雖然C程序總是從main函數(shù)開始執(zhí)行缓熟,但是內(nèi)核使用exec函數(shù)執(zhí)行C程序時累魔,在調(diào)用main前會先調(diào)用一個啟動例程,這個啟動例程就是_start够滑。以下面這段代碼為例垦写,通過gcc test.c -o test編譯生成可執(zhí)行文件test。
/*test.c*/
int main()
{
return 0;
}
使用objdump對test進(jìn)行分析彰触,得到反匯編結(jié)果如下梯澜,可以看出main函數(shù)地址為0x4004b4,在_start有去調(diào)用到mov $0x4004b4,%rdi渴析。這個示例只是為了證明main函數(shù)是由啟動例程_start調(diào)用的,此處不過多的去探究反匯編得到的這些符號的含義吮龄,有興趣的同學(xué)可以參考 程序員的自我修養(yǎng):鏈接俭茧、裝載與庫中第11章的第1節(jié) 入口函數(shù)和程序初始化。
ckt@ubuntu:~/work/unix/code/chapter7$ objdump -S -d signal.o
... ...
00000000004003d0 <_start>:
4003d0: 31 ed xor %ebp,%ebp
4003d2: 49 89 d1 mov %rdx,%r9
4003d5: 5e pop %rsi
4003d6: 48 89 e2 mov %rsp,%rdx
4003d9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
4003dd: 50 push %rax
4003de: 54 push %rsp
4003df: 49 c7 c0 50 05 40 00 mov $0x400550,%r8
4003e6: 48 c7 c1 c0 04 40 00 mov $0x4004c0,%rcx
4003ed: 48 c7 c7 b4 04 40 00 mov $0x4004b4,%rdi
4003f4: e8 c7 ff ff ff callq 4003c0 <__libc_start_main@plt>
4003f9: f4 hlt
4003fa: 90 nop
4003fb: 90 nop
... ...
00000000004004b4 <main>:
4004b4: 55 push %rbp
4004b5: 48 89 e5 mov %rsp,%rbp
4004b8: b8 00 00 00 00 mov $0x0,%eax
4004bd: 5d pop %rbp
4004be: c3 retq
4004bf: 90 nop
... ...
1.2 命令行參數(shù)
接著來看main函數(shù)的參數(shù)漓帚,main函數(shù)原型如下
int main(int argc, char const *argv[])
通過命令行運(yùn)行一個C程序時母债,我們可以通過命令行傳入?yún)?shù)。main函數(shù)中的參數(shù)argc表示參數(shù)個數(shù)尝抖,argv中存放的是參數(shù)值毡们。
-
示例
使用如下代碼打印出從命令行傳入的參數(shù)
#include "apue.h"
int main(int argc, char const *argv[])
{
int i = 0;
for (i = 0; i < argc; i++)
printf("%s\n", argv[i]);
return 0;
}
- 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter7$ cc get_arg.c -o get_arg
ckt@ubuntu:~/work/unix/code/chapter7$ ./get_arg
./get_arg
ckt@ubuntu:~/work/unix/code/chapter7$ ./get_arg test1 test2 test3
./get_arg
test1
test2
test3
1.3 環(huán)境表
大多數(shù)UNIX系統(tǒng)支持main函數(shù)帶3個參數(shù),其中第3個參數(shù)envp就是環(huán)境變量表的地址昧辽,和argv一樣衙熔,evnp也是一個數(shù)組
int main(int argc, char const *argv[], char *envp[])
-
示例
打印出當(dāng)前程序的環(huán)境變量表
#include "apue.h"
void printinfo(char * info)
{
int i = 0;
while (info[i] != '\0')
{
printf("%c", info[i]);
i++;
}
printf(" ");
}
int main(int argc, char const *argv[], char *envp[])
{
while (*envp != NULL)
{
printinfo(*envp);
envp++;
}
printf("\n");
return 0;
}
-
運(yùn)行結(jié)果
由于打印的環(huán)境變量值太多,省略部分打印結(jié)果搅荞。
ckt@ubuntu:~/work/unix/code/chapter7$ cc get_environ.c -o get_environ
ckt@ubuntu:~/work/unix/code/chapter7$ ./get_environ
SSH_AGENT_PID=2030 SHELL=/bin/bash OLDPWD=/home/ckt/work/unix/code/chapter10
LANG=en_US.UTF-8 HOME=/home/ckt _=./get_environ
2. C程序的存儲空間布局
C程序通常由5個部分組成:
- 正文段(.text):存放CPU的機(jī)器指令
- 初始化數(shù)據(jù)段(.data):存放全局靜態(tài)變量和局部靜態(tài)變量
- 未初始化數(shù)據(jù)段(.bss):存未初始化的全局變量和局部靜態(tài)變量
- 棧(stack):存放局部變量和函數(shù)調(diào)用時所需保存的信息
- 堆(heap):動態(tài)分配的變量
我們同樣可以通過objdump去分析各個段红氯,如果想要了解更多細(xì)節(jié),請參考使用readelf和objdump解析目標(biāo)文件
3. 共享庫
共享庫使得可執(zhí)行文件中不再需要包含公用的庫函數(shù)咕痛,只需要在進(jìn)程可引用的儲存區(qū)保存這個庫例程的一個副本痢甘。共享庫有以下兩個優(yōu)點(diǎn):
1. 減少了可執(zhí)行文件的長度
2. 可以用庫函數(shù)的新版本代替老版本,而無需對使用該庫的程序重新連接編輯
- 示例
先用無共享方式創(chuàng)建可執(zhí)行文件a.out茉贡,并使用size命令查看正文段和數(shù)據(jù)段的長度
ckt@ubuntu:~/work/unix/code/chapter7$ gcc -static test.c
ckt@ubuntu:~/work/unix/code/chapter7$ ls -l a.out
-rwxrwxr-x 1 ckt ckt 883375 Apr 24 04:30 a.out
ckt@ubuntu:~/work/unix/code/chapter7$ size a.out
text data bss dec hex filename
790568 6128 11272 807968 c5420 a.out
再使用共享庫編譯此程序塞栅,得到的可執(zhí)行文件的正文和數(shù)據(jù)段長度明顯減小
ckt@ubuntu:~/work/unix/code/chapter7$ gcc test.c
ckt@ubuntu:~/work/unix/code/chapter7$ ls -l a.out
-rwxrwxr-x 1 ckt ckt 8326 Apr 24 04:30 a.out
ckt@ubuntu:~/work/unix/code/chapter7$ size a.out
text data bss dec hex filename
1076 496 16 1588 634 a.out
4. 動態(tài)存儲空間分配
5. 環(huán)境變量
當(dāng)我們登錄Linux之后,使用shell來跟系統(tǒng)進(jìn)行通信腔丧,在進(jìn)入shell之前放椰,系統(tǒng)需要一些變量來提供它的數(shù)據(jù)訪問(如是否顯示彩色作烟、當(dāng)前路徑和主文件夾的路徑等),這些變量就是環(huán)境變量庄敛。為了區(qū)別于自定義變量俗壹,環(huán)境變量通常用大寫字符表示。
5.1 終端中獲取和設(shè)置環(huán)境變量
使用env命令可以列出當(dāng)前shell環(huán)境下的所有環(huán)境變量
ckt@ubuntu:~/work/unix/code/chapter7$ env
SSH_AGENT_PID=2030
GPG_AGENT_INFO=/tmp/keyring-NWSXuo/gpg:0:1
TERM=xterm
SHELL=/bin/bash
XDG_SESSION_COOKIE=b2968a33ddb5564574638ff00000000f-1492995827.229855-1165390831
WINDOWID=71303173
OLDPWD=/home/ckt/work/unix/code/chapter10
GNOME_KEYRING_CONTROL=/tmp/keyring-NWSXuo
USER=ckt
... ...
使用echo $環(huán)境變量名命令可以查看單個環(huán)境變量藻烤,例如绷雏,當(dāng)前路徑保存在環(huán)境變量PWD中
ckt@ubuntu:~/work/unix/code/chapter7$ echo $PWD
/home/ckt/work/unix/code/chapter7
使用PATH=$PATH:/home/ckt/work/unix/給PTAH變量追加路徑/home/ckt/work/unix/
ckt@ubuntu:~/work/unix/code/chapter7$ echo $PATH
/home/ckt/work/msm8939-la-2-1/LINUX/android/out/host/linux-x86/bin:/home/ckt/work/adt-bundle-linux-x86_64-20140702/sdk/platform-tools:/home/ckt/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
ckt@ubuntu:~/work/unix/code/chapter7$ PATH=$PATH:/home/ckt/work/unix/
ckt@ubuntu:~/work/unix/code/chapter7$ echo $PATH
/home/ckt/work/msm8939-la-2-1/LINUX/android/out/host/linux-x86/bin:/home/ckt/work/adt-bundle-linux-x86_64-20140702/sdk/platform-tools:/home/ckt/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/ckt/work/unix/
ckt@ubuntu:~/work/unix/code/chapter7$
5.2 使用getenv獲得環(huán)境變量
- 示例
#include "apue.h"
int main(int argc, char const *argv[])
{
char * home = NULL;
char * shell = NULL;
char * path = NULL;
if ((home = getenv("HOME")) != NULL)
printf("%s\n", home);
if ((shell = getenv("SHELL")) != NULL)
printf("%s\n", shell);
if ((path = getenv("PATH")) != NULL)
printf("%s\n", path);
return 0;
}
- 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter7$ cc getenv_test.c -o getenv_test
ckt@ubuntu:~/work/unix/code/chapter7$ ./getenv_test
/home/ckt
/bin/bash
/home/ckt/work/msm8939-la-2-1/LINUX/android/out/host/linux-x86/bin:/home/ckt/work/adt-bundle-linux-x86_64-20140702/sdk/platform-tools:/home/ckt/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
5.3 通過函數(shù)設(shè)置環(huán)境變量
- 示例
#include "apue.h"
void print_pwd(char * info)
{
char * pwd = NULL;
printf("%s : ", info);
if ((pwd = getenv("PWD")) != NULL)
printf("%s\n", pwd);
else
printf("pwd is NULL \n");
}
int main(int argc, char const *argv[])
{
print_pwd("before setenv");
if (setenv("PWD", "test", 1) < 0)
err_sys("setenv error");
print_pwd("after setenv");
if (unsetenv("PWD") < 0)
err_sys("unsetenv error");
print_pwd("after unsetenv");
return 0;
}
- 運(yùn)行結(jié)果
ckt@ubuntu:~/work/unix/code/chapter7$ cc setenv_test.c -o setenv_test
ckt@ubuntu:~/work/unix/code/chapter7$ ./setenv_test
before setenv : /home/ckt/work/unix/code/chapter7
after setenv : test
after unsetenv : pwd is NULL
6. 進(jìn)程終止
參考
- 《UNIX環(huán)境高級編程(第3版)》第7章 進(jìn)程環(huán)境
- 《鳥哥的Linux私房菜(第三版)》 第11章 認(rèn)識和學(xué)習(xí) BASH
- 《程序員的自我修養(yǎng):鏈接、裝載與庫》第3章 目標(biāo)文件里面有什么
- 《程序員的自我修養(yǎng):鏈接怖亭、裝載與庫》第11章 運(yùn)行庫
- main函數(shù)和啟動例程