本文內(nèi)容:
1. 進程產(chǎn)生方式
2. 進程間通信和同步
3. 線程
1. 進程產(chǎn)生方式
產(chǎn)生一個進程有多種方式仁热,如fork()香缺, system(), exec()函數(shù)等吱雏。
1.1 fork()
簡介
#include <unistd.h>
#include <sys/types.h>
pid_t fork (void);
創(chuàng)建與當(dāng)前進程幾乎完全相同的子進程盹憎。
調(diào)用fork函數(shù)后筛峭,系統(tǒng)給新進程分配資源,例如存儲數(shù)據(jù)與代碼空間陪每,然后把原來進程的所有值都復(fù)制到新進程中影晓,只有少數(shù)的值與原進程不同。
fork()之后檩禾,子進程與父進程中的變量名相同挂签,但不共享,子進程或父進程中改變某變量的值不會影響另一個進程中相同名稱的變量值盼产。
- 調(diào)用失敗返回小于0的值:
errno=EAGAIN饵婆,當(dāng)前的進程數(shù)已經(jīng)達到系統(tǒng)上限。
errno=ENOMEM,系統(tǒng)內(nèi)存不足戏售。 - 調(diào)用成功返回兩個值:
pid_t=0:子進程空間
pid_t>0:父進程空間
調(diào)用成功后侨核,子進程和父進程都要執(zhí)行fork以后的語句,通過判斷fork的返回值來區(qū)分父進程和子進程灌灾。
fork()例程
#include <unistd.h>
#include <stdio.h>
int main(){
pid_t fpid;
int count =0;
fpid=fork();
if(fpid<0)//調(diào)用失敗
printf("error in fork!");
else if(fpid==0) { /子進程要執(zhí)行的部分
printf("i am the child process,my pid is %d\n",getpid());
count++;
}
else { //父進程要執(zhí)行的部分
printf("i am the parent process,my pid is %d\n",getpid());
count++:
}
printf("count is:%d\n",count);
return 0;
}
執(zhí)行過程:
執(zhí)行結(jié)果:
i am the child process,my pid is 3
count is 1
i am the parent process ,my pid is 2
count is 1
由于這兩個進程是獨立的搓译,存在于不同的地址空間,因此count變量不是共用的
參考:https://blog.csdn.net/jason314/article/details/5640969
1.2 system()
簡介
system()函數(shù)調(diào)用外部shell命令在當(dāng)前進程中開始另一個進程锋喜,阻塞當(dāng)前進程侥衬,直到外部命令執(zhí)行完畢。
#include <stdlob.h>
int system(const char command);
//command為需要執(zhí)行的外部命令
執(zhí)行system()函數(shù)時跑芳,會調(diào)用fork()轴总、 execve()、 waitpid()等函數(shù)博个,其中任意一個調(diào)用失敗將會導(dǎo)致system()調(diào)用失敗怀樟。
返回值:
- 失敗:返回-1盆佣;
- command不能執(zhí)行:返回127往堡;
- 成功:返回進程狀態(tài)值。
system()例程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int ret;
printf("pid:%d \n",getpid());
ret=system("mkdir test");
printf("ret:%d \n",ret);
return 0;
}
執(zhí)行結(jié)果:
當(dāng)前進程的ID為3368共耍,system()執(zhí)行“mkdir test”命令后返回值為0.
執(zhí)行結(jié)束后虑灰,當(dāng)前目錄下出現(xiàn)了新建的目錄test。
1.3 exec()
簡介
exec()族函數(shù)公有6個:
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char const *envp[]);
int execve(const char *path, const char *arg[]);
int execvp(const char *path, const char *arg[]);
只有esecve()是真正的系統(tǒng)調(diào)用痹兜,其他都是此基礎(chǔ)上進行包裝的庫函數(shù)穆咐。
exec()函數(shù)族的作用是根據(jù)指定的文件名找到可執(zhí)行文件名,并在原來的進程內(nèi)部執(zhí)行此可執(zhí)行文件字旭。
exec()協(xié)議族調(diào)用成功不會返回对湃,失敗則會返回-1。
在Linux系統(tǒng)中遗淳,調(diào)用fork()之后進行exec()系統(tǒng)調(diào)用拍柒,系統(tǒng)就不會對父進程進行復(fù)制,而是直接使用exec()指定的參數(shù)覆蓋原有進程屈暗,利于節(jié)省時間拆讯。
exec()族成員函數(shù)介紹
- execl()
#include <unistd.h>
int execl(
const char *path, //要執(zhí)行命令的路徑
const char *arg, //要啟動程序所帶的參數(shù),一般第一個參數(shù)為要執(zhí)行的命令名
.../* (char *) NULL */ //命令參數(shù)养叛,最后一個必須為NULL
);
將當(dāng)前的進程替換成一個新的進程种呐,
執(zhí)行到exec()函數(shù)時當(dāng)前進程就會結(jié)束新進程則開始執(zhí)行。但新進程保留之前進程的進程號
執(zhí)行成功返回0一铅,失敗返回-1并設(shè)置errno信息
參考網(wǎng)站:https://www.cnblogs.com/mickole/p/3187409.html
execlp()
execle()
execve()
execvp()
2. 進程間通信和同步
消息隊列
msgrcv
include <sys/types.h>
include <sys/ipc.h>
include <sys/msg.h>
int msgrcv(
int msgid,//消息隊列標識
void *msgp,//存放消息的結(jié)構(gòu)體陕贮,類型要與msgsnd函數(shù)發(fā)送的類型相同
size_t msgsz,//接受消息的大小,不含消息類型占用的4個字節(jié)
long msgtyp;//接受類型
int msgflg;//接受方式
);
//調(diào)用成功返回讀取到的消息長度潘飘,失敗返回錯誤碼
msgtyp的值 | 含義 |
---|---|
0 | 接受第一個消息 |
>0 | 接受類型等于msgtyp的第一個消息 |
<0 | 接受類型等于或小于msgtyp絕對值的第一個消息 |
msgflg的值 | 含義 |
---|---|
0 | 阻塞式肮之,沒有該類型的消息則一直等待 |
IPC_NOWAIT | 如果沒有滿足條件的消息則立即返回,錯誤碼為ENOMSG |
IPC_EXCEPT | 與msgtyp配合是卜录,返回隊列中一地個類型不為msgtyp的消息 |
IPC_NOERROR | 如果滿足條件的消息內(nèi)容大于size戈擒,則把消息截斷,截斷部分丟棄 |
解除阻塞的條件有三個:
1.消息隊列中有了滿足條件的消息
2.msgid代表的消息隊列被刪除
3.調(diào)用msgrcv的進程被信號中斷
錯誤碼 | 含義 |
---|---|
E2BIG | 消息長度大于size艰毒,而沒有設(shè)置IPC_NOERROR |
EIDRM | 標識為msgid的消息隊列已被刪除 |
EACCESS | 沒有讀取權(quán)限 |
EFAULT | msgp指向無效的內(nèi)存地址 |
ENOMSG | msgflg為IPC_NOWAIT筐高,而隊列中無消息可讀 |
EINTR | 等待讀取隊列的消息是被信號中斷 |
有關(guān)消息隊列的其他函數(shù)見:
https://blog.csdn.net/guoping16/article/details/6584024
waitpid
#include <sys/types.h>
#incude <sys/wait.h>
pid_t waitpid(
pid_t pid,//進程ID
int *status,//返回參數(shù)
int options//調(diào)用選項
);
暫時停止目前進程的執(zhí)行,直到有信號到來或子進程結(jié)束。
pid
pid>0 :只等待ID等于pid的子進程
pid=0 :等待同一個進程組中的任何子進程
pid=-1 :等待任何一個子進程退出
pid<-1 :等待一個指定進程組中的任何子進程退出柑土,這個進程組的ID等于pid的絕對值status
返回子進程的結(jié)束狀態(tài)和進程識別碼蜀肘,可以設(shè)置為NULL-
option
option=0:不使用此功能
option=WNOHANG:即使沒有子進程退出也會立即返回
option=WUNTRACED:極少用
信號
killinclude <sys/types.h> include <signal.h> int kill(//用于向任何進程或進程組發(fā)送信號 pid_t pid,//進程號 int sig//準備發(fā)送的信號代碼 );
參考:https://www.cnblogs.com/leeming0222/articles/3994125.html
signal
#include <signal.h>
typedef void(*sighandler_t) int
sighandler_t signal(
int signum,//要處理的信號類型,可以取除了SIGKILL和SIGSTOP外的任何一種信號
sighandler_t handler//與信號關(guān)聯(lián)的動作
);
當(dāng)進程接收到類型為signal的信號時稽屏,不管程序執(zhí)行到那一部分扮宠,立即執(zhí)行handler動作。執(zhí)行結(jié)束后狐榔,控制權(quán)返回進程被中斷的一點繼續(xù)進行坛增。
參考:https://blog.csdn.net/yockie/article/details/51729774
#include <signal.h>
int sigempty(sigset_t *set);
//初始化set指向的信號集,清除其中的所有信號
int sigfillset(sigset_t *set);
//初始化set指向的信號集,使其包含所有信號
int sigaddset(sigset_t *set, int signo);
//在信號集中添加一個信號
int sigdeleset(sigset_t *set, int signo);
//在信號集中刪除一個信號
int sigismember(sigset_t *set, int signo);
//判斷某信號是否存在信號集中
參考:http://blog.sina.com.cn/s/blog_6f916d330100ycnh.html
3. 線程
參考文獻
- 《Linux網(wǎng)絡(luò)編程(第2版)》薄腻,宋敬彬等編著收捣。