[ 永遠(yuǎn)的UNIX > Linux下C語(yǔ)言編程--進(jìn)程的創(chuàng)建 ]
首頁(yè) > 編程技術(shù) > C/C++ > 正文
Linux下C語(yǔ)言編程--進(jìn)程的創(chuàng)建
http://linuxc. 作者:hoyt (2001-05-08 11:32:30)
前言:
這篇文章是用來(lái)介紹在Linux下和進(jìn)程相關(guān)的各個(gè)概念.我們將會(huì)學(xué)到:
1.進(jìn)程的概念
2.進(jìn)程的身份
3.進(jìn)程的創(chuàng)建
4.守護(hù)進(jìn)程的創(chuàng)建
--------------------------------------------------------------------------------
1。進(jìn)程的概念
Linux操作系統(tǒng)是面向多用戶的.在同一時(shí)間可以有許多用戶向操作系統(tǒng)發(fā)出各種命令.那么操作系統(tǒng)是怎么實(shí)現(xiàn)多用戶的環(huán)境呢?
在現(xiàn)代的操作系統(tǒng)里面,都有程序和進(jìn)程的概念.那么什么是程序,什么是進(jìn)程呢?
通俗的講程序是一個(gè)包含可以執(zhí)行代碼的文件,是一個(gè)靜態(tài)的文件.而進(jìn)程是一個(gè)開(kāi)始執(zhí)行但是還沒(méi)有結(jié)束的程序的實(shí)例.就是可執(zhí)行文件的具體實(shí)現(xiàn).
一個(gè)程序可能有許多進(jìn)程,而每一個(gè)進(jìn)程又可以有許多子進(jìn)程.依次循環(huán)下去,而產(chǎn)生子孫進(jìn)程.
當(dāng)程序被系統(tǒng)調(diào)用到內(nèi)存以后,系統(tǒng)會(huì)給程序分配一定的資源(內(nèi)存,設(shè)備等等)然后進(jìn)行一系列的復(fù)雜操作,使程序變成進(jìn)程以供系統(tǒng)調(diào)用.在系統(tǒng)里面只有進(jìn)程沒(méi)有程序,為了區(qū)分各個(gè)不同的進(jìn)程,系統(tǒng)給每一個(gè)進(jìn)程分配了一個(gè)ID(就象我們的身份證)以便識(shí)別.
為了充分的利用資源,系統(tǒng)還對(duì)進(jìn)程區(qū)分了不同的狀態(tài).將進(jìn)程分為新建,運(yùn)行,阻塞,就緒和完成五個(gè)狀態(tài).
新建表示進(jìn)程正在被創(chuàng)建,運(yùn)行是進(jìn)程正在運(yùn)行,阻塞是進(jìn)程正在等待某一個(gè)事件發(fā)生,就緒是表示系統(tǒng)正在等待CPU來(lái)執(zhí)行命令,而完成表示進(jìn)程已經(jīng)結(jié)束了系統(tǒng)正在回收資源.
關(guān)于進(jìn)程五個(gè)狀態(tài)的詳細(xì)解說(shuō)我們可以看《操作系統(tǒng)》上面有詳細(xì)的解說(shuō)。
2。進(jìn)程的標(biāo)志
上面我們知道了進(jìn)程都有一個(gè)ID,那么我們?cè)趺吹玫竭M(jìn)程的ID呢?系統(tǒng)調(diào)用getpid可以得到進(jìn)程的ID,而getppid可以得到父進(jìn)程(創(chuàng)建調(diào)用該函數(shù)進(jìn)程的進(jìn)程)的ID.
#include
pid_t getpid(void);
pid_t getppid(void);
進(jìn)程是為程序服務(wù)的,而程序是為了用戶服務(wù)的.系統(tǒng)為了找到進(jìn)程的用戶名,還為進(jìn)程和用戶建立聯(lián)系.這個(gè)用戶稱為進(jìn)程的所有者.相應(yīng)的每一個(gè)用戶也有一個(gè)用戶ID.通過(guò)系統(tǒng)調(diào)用getuid可以得到進(jìn)程的所有者的ID.由于進(jìn)程要用到一些資源,而Linux對(duì)系統(tǒng)資源是進(jìn)行保護(hù)的,為了獲取一定資源進(jìn)程還有一個(gè)有效用戶ID.這個(gè)ID和系統(tǒng)的資源使用有關(guān),涉及到進(jìn)程的權(quán)限.
通過(guò)系統(tǒng)調(diào)用geteuid我們可以得到進(jìn)程的有效用戶ID.
和用戶ID相對(duì)應(yīng)進(jìn)程還有一個(gè)組ID和有效組ID系統(tǒng)調(diào)用getgid和getegid可以分別得到組ID和有效組ID
#include
#include
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
git_t getegid(void);
有時(shí)候我們還會(huì)對(duì)用戶的其他信息感興趣(登錄名等等),這個(gè)時(shí)候我們可以調(diào)用getpwuid來(lái)得到.
struct passwd {
char *pw_name; /* 登錄名稱 */
char *pw_passwd; /* 登錄口令 */
uid_t pw_uid; /* 用戶ID */
gid_t pw_gid; /* 用戶組ID */
char *pw_gecos; /* 用戶的真名 */
char *pw_dir; /* 用戶的目錄 */
char *pw_shell; /* 用戶的SHELL */
};
#include
#include
struct passwd *getpwuid(uid_t uid);
下面我們學(xué)習(xí)一個(gè)實(shí)例來(lái)實(shí)踐一下上面我們所學(xué)習(xí)的幾個(gè)函數(shù):
#include
#include
#include
#include
int main(int argc,char **argv)
{
pid_t my_pid,parent_pid;
uid_t my_uid,my_euid;
gid_t my_gid,my_egid;
struct passwd *my_info;
my_pid=getpid();
parent_pid=getppid();
my_uid=getuid();
my_euid=geteuid();
my_gid=getgid();
my_egid=getegid();
my_info=getpwuid(my_uid);
printf("Process ID:%ld\n",my_pid);
printf("Parent ID:%ld\n",parent_pid);
printf("User ID:%ld\n",my_uid);
printf("Effective User ID:%ld\n",my_euid);
printf("Group ID:%ld\n",my_gid);
printf("Effective Group ID:%ld\n",my_egid):
if(my_info)
{
printf("My Login Name:%s\n" ,my_info->pw_name);
printf("My Password :%s\n" ,my_info->pw_passwd);
printf("My User ID :%ld\n",my_info->pw_uid);
printf("My Group ID :%ld\n",my_info->pw_gid);
printf("My Real Name:%s\n" ,my_info->pw_gecos);
printf("My Home Dir :%s\n", my_info->pw_dir);
printf("My Work Shell:%s\n", my_info->pw_shell);
}
}
3。進(jìn)程的創(chuàng)建
創(chuàng)建一個(gè)進(jìn)程的系統(tǒng)調(diào)用很簡(jiǎn)單.我們只要調(diào)用fork函數(shù)就可以了.
#include
pid_t fork();
當(dāng)一個(gè)進(jìn)程調(diào)用了fork以后,系統(tǒng)會(huì)創(chuàng)建一個(gè)子進(jìn)程.這個(gè)子進(jìn)程和父進(jìn)程不同的地方只有他的進(jìn)程ID和父進(jìn)程ID,其他的都是一樣.就象符進(jìn)程克隆(clone)自己一樣.當(dāng)然創(chuàng)建兩個(gè)一模一樣的進(jìn)程是沒(méi)有意義的.為了區(qū)分父進(jìn)程和子進(jìn)程,我們必須跟蹤fork的返回值.
當(dāng)fork掉用失敗的時(shí)候(內(nèi)存不足或者是用戶的最大進(jìn)程數(shù)已到)fork返回-1,否則fork的返回值有重要的作用.對(duì)于父進(jìn)程fork返回子進(jìn)程的ID,而對(duì)于fork子進(jìn)程返回0.我們就是根據(jù)這個(gè)返回值來(lái)區(qū)分父子進(jìn)程的.
父進(jìn)程為什么要?jiǎng)?chuàng)建子進(jìn)程呢?前面我們已經(jīng)說(shuō)過(guò)了Linux是一個(gè)多用戶操作系統(tǒng),在同一時(shí)間會(huì)有許多的用戶在爭(zhēng)奪系統(tǒng)的資源.有時(shí)進(jìn)程為了早一點(diǎn)完成任務(wù)就創(chuàng)建子進(jìn)程來(lái)爭(zhēng)奪資源.
一旦子進(jìn)程被創(chuàng)建,父子進(jìn)程一起從fork處繼續(xù)執(zhí)行,相互競(jìng)爭(zhēng)系統(tǒng)的資源.有時(shí)候我們希望子進(jìn)程繼續(xù)執(zhí)行,而父進(jìn)程阻塞直到子進(jìn)程完成任務(wù).這個(gè)時(shí)候我們可以調(diào)用wait或者waitpid系統(tǒng)調(diào)用.
#include
#include
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid,int *stat_loc,int options);
wait系統(tǒng)調(diào)用會(huì)使父進(jìn)程阻塞直到一個(gè)子進(jìn)程結(jié)束或者是父進(jìn)程接受到了一個(gè)信號(hào).如果沒(méi)有父進(jìn)程沒(méi)有子進(jìn)程或者他的子進(jìn)程已經(jīng)結(jié)束了wait回立即返回.成功時(shí)(因一個(gè)子進(jìn)程結(jié)束)wait將返回子進(jìn)程的ID,否則返回-1,并設(shè)置全局變量errno.stat_loc是子進(jìn)程的退出狀態(tài).子進(jìn)程調(diào)用exit,_exit
或者是return來(lái)設(shè)置這個(gè)值. 為了得到這個(gè)值Linux定義了幾個(gè)宏來(lái)測(cè)試這個(gè)返回值.
WIFEXITED:判斷子進(jìn)程退出值是非0
WEXITSTATUS:判斷子進(jìn)程的退出值(當(dāng)子進(jìn)程退出時(shí)非0).
WIFSIGNALED:子進(jìn)程由于有沒(méi)有獲得的信號(hào)而退出.
WTERMSIG:子進(jìn)程沒(méi)有獲得的信號(hào)號(hào)(在WIFSIGNALED為真時(shí)才有意義).
waitpid等待指定的子進(jìn)程直到子進(jìn)程返回.如果pid為正值則等待指定的進(jìn)程(pid).如果為0則等待任何一個(gè)組ID和調(diào)用者的組ID相同的進(jìn)程.為-1時(shí)等同于wait調(diào)用.小于-1時(shí)等待任何一個(gè)組ID等于pid絕對(duì)值的進(jìn)程.
stat_loc和wait的意義一樣. options可以決定父進(jìn)程的狀態(tài).可以取兩個(gè)值
WNOHANG:父進(jìn)程立即返回當(dāng)沒(méi)有子進(jìn)程存在時(shí). WUNTACHED:當(dāng)子進(jìn)程結(jié)束時(shí)waitpid返回,但是子進(jìn)程的退出狀態(tài)不可得到.
父進(jìn)程創(chuàng)建子進(jìn)程后,子進(jìn)程一般要執(zhí)行不同的程序.為了調(diào)用系統(tǒng)程序,我們可以使用系統(tǒng)調(diào)用exec族調(diào)用.exec族調(diào)用有著5個(gè)函數(shù).
#include
int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
int execle(const char *path,const char *arg,...);
int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv[]):
exec族調(diào)用可以執(zhí)行給定程序.關(guān)于exec族調(diào)用的詳細(xì)解說(shuō)可以參考系統(tǒng)手冊(cè)(man execl).
下面我們來(lái)學(xué)習(xí)一個(gè)實(shí)例.注意編譯的時(shí)候要加 -lm以便連接數(shù)學(xué)函數(shù)庫(kù).
#include
#include
#include
#include
#include
#include
void main(void)
{
pid_t child;
int status;
printf("This will demostrate how to get child status\n");
if((child=fork())==-1)
{
printf("Fork Error :%s\n",strerror(errno));
exit(1);
}
else if(child==0)
{
int i;
printf("I am the child:%ld\n",getpid());
for(i=0;i<1000000;i++) sin(i);
i=5;
printf("I exit with %d\n",i);
exit(i);
}
while(((child=wait(&status))==-1)&(errno==EINTR));
if(child==-1)
printf("Wait Error:%s\n",strerror(errno));
else if(!status)
printf("Child %ld terminated normally return status is zero\n",
child);
else if(WIFEXITED(status))
printf("Child %ld terminated normally return status is %d\n",
child,WEXITSTATUS(status));
else if(WIFSIGNALED(status))
printf("Child %ld terminated due to signal %d znot caught\n",
child,WTERMSIG(status));
}
strerror函數(shù)會(huì)返回一個(gè)指定的錯(cuò)誤號(hào)的錯(cuò)誤信息的字符串.
4。守護(hù)進(jìn)程的創(chuàng)建
如果你在DOS時(shí)代編寫過(guò)程序,那么你也許知道在DOS下為了編寫一個(gè)常駐內(nèi)存的程序我們要編寫多少代碼了.相反如果在Linux下編寫一個(gè)"常駐內(nèi)存"的程序卻是很容易的.我們只要幾行代碼就可以做到.
實(shí)際上由于Linux是多任務(wù)操作系統(tǒng),我們就是不編寫代碼也可以把一個(gè)程序放到后臺(tái)去執(zhí)行的.我們只要在命令后面加上&符號(hào)SHELL就會(huì)把我們的程序放到后臺(tái)去運(yùn)行的.
這里我們"開(kāi)發(fā)"一個(gè)后臺(tái)檢查郵件的程序.這個(gè)程序每個(gè)一個(gè)指定的時(shí)間回去檢查我們的郵箱,如果發(fā)現(xiàn)我們有郵件了,會(huì)不斷的報(bào)警(通過(guò)機(jī)箱上的小喇叭來(lái)發(fā)出聲音).
后面有這個(gè)函數(shù)的加強(qiáng)版本加強(qiáng)版本
后臺(tái)進(jìn)程的創(chuàng)建思想: 首先父進(jìn)程創(chuàng)建一個(gè)子進(jìn)程.然后子進(jìn)程殺死父進(jìn)程(是不是很無(wú)情?). 信號(hào)處理所有的工作由子進(jìn)程來(lái)處理.
#include
#include
#include
#include
#include
#include
#include
/* Linux 的默任個(gè)人的郵箱地址是 /var/spool/mail/用戶的登錄名 */
#define MAIL "/var/spool/mail/hoyt"
/* 睡眠10秒鐘 */
#define SLEEP_TIME 10
main(void)
{
pid_t child;
if((child=fork())==-1)
{
printf("Fork Error:%s\n",strerror(errno));
exit(1);
}
else if(child>0)
while(1);
if(kill(getppid(),SIGTERM)==-1)
{
printf("Kill Parent Error:%s\n",strerror(errno));
exit(1);
}
{
int mailfd;
while(1)
{
if((mailfd=open(MAIL,O_RDONLY))!=-1)
{
fprintf(stderr,"%s","\007");
close(mailfd);
}
sleep(SLEEP_TIME);
}
}
}
你可以在默認(rèn)的路徑下創(chuàng)建你的郵箱文件,然后測(cè)試一下這個(gè)程序.當(dāng)然這個(gè)程序還有很多地方要改善的.我們后面會(huì)對(duì)這個(gè)小程序改善的,再看我的改善之前你可以嘗試自己改善一下.比如讓用戶指定郵相的路徑和睡眠時(shí)間等等.相信自己可以做到的.動(dòng)手吧,勇敢的探險(xiǎn)者.
好了進(jìn)程一節(jié)的內(nèi)容我們就先學(xué)到這里了.進(jìn)程是一個(gè)非常重要的概念,許多的程序都會(huì)用子進(jìn)程.創(chuàng)建一個(gè)子進(jìn)程是每一個(gè)程序員的基本要求!
--------------------------------------------------------------------------------
(http://www.) 進(jìn)入【UNIX論壇】
相關(guān)文章
Linux下C語(yǔ)言編程--線程操作 (2001-05-08 11:43:15)
Linux下C語(yǔ)言編程--進(jìn)程通信、消息管理 (2001-05-08 11:38:03)
Linux下C語(yǔ)言編程--信號(hào)處理函數(shù) (2001-05-08 11:35:28)
Linux下C語(yǔ)言編程--時(shí)間概念 (2001-05-08 11:34:12)
Linux下C語(yǔ)言編程--文件的操作 (2001-05-08 11:33:15)
Linux下C語(yǔ)言編程--進(jìn)程的創(chuàng)建 (2001-05-08 11:32:30)
Linux下C語(yǔ)言編程--基礎(chǔ)知識(shí) (2001-05-08 11:31:29)