感覺需要說一下,概念性的東西,都隻是個人了解.
EXEC函數族
頭檔案
#include <unistd.h>
execlp(加載一個程序,借助PATH)
原型
int execlp(const char *file, const char *arg, ...);
傳回值
成功無傳回
失敗傳回-1
函數後直接跟(無需判斷,因為沒有傳回,如果成功後續代碼不會執行)
perror();
exit(1);
file 可執行檔案名
arg 參數(從argv[0]開始),也就是需要傳入兩個可執行檔案名
execlp("ls", "ls", "-l",NULL);
參數最後一個為NULL
隻能調用PATH環境變量中的,ls cp等
案例
#include <unistd.h>
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
pid_t pid = fork();
if (pid == -1)
{
perror("pid_t pid = fork();");
exit(1);
}
if (pid == 0)
{
// arg的第一個參數必須是檔案名
// 函數的最後要給參數必須是NULL
execlp("ls", "ls", "-l", NULL);
// 因為函數成功無傳回,是以當execlp函數執行成功後續代碼都不會執行
perror("execlp(ls, ls, - l, NULL);");
exit(1);
}
if (pid > 0)
{
sleep(1);
cout << "parent process finish" << endl;
}
return 0;
}
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiYTMfhHLlN3XnxCM38FdsYkRGZkRG9lcvx2bjxyNx8VZ6l2cs0TPB5EeNRVT0AHSidHczMWQClGVF5UMR9Fd4VGdsATNfd3bkFGazxycykFaKdkYzZUbapXNXlleSdVY2pESa9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzYmMyMDO0ATY4QjZzIjZ5kDZmRTMyMjMkBTOwQWOidzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
execl(執行自己的程式)這個函數也可以執行PATH指定路徑就可以了
原型
int execl(const char *pathname, const char *arg, ...);
第一個參數傳入的是自己的程式路徑其他參數一樣
案例
#include <iostream>
#include <unistd.h>
using namespace std;
int main(int argc, char** argv)
{
pid_t pid = fork();
if (pid == -1)
{
perror("pid_t pid = fork();");
exit(1);
}
if (pid > 0)
{
cout << "parent process finish" << endl;
}
if (pid == 0)
{
execl("./execlp", "./execlp", NULL);
// 執行系統PATH
// execl("/bin/ls", "ls", NULL);
perror("execl");
exit(1);
}
return 0;
}
execvp(第二個參數是指針數組,p就是PATH的首字母)
原型
int execv(const char *pathname, char *const argv[]);
第二個參數需要單獨建立一個指針數組,數組元素就是execlp的arg參數
案例
#include <unistd.h>
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
pid_t pid = fork();
if (pid == -1)
{
perror("pid_t pid = fork();");
exit(1);
}
if (pid == 0)
{
// 參數二需要用的數組(這個東西是叫指針數組還是數組指針真的取名這東西就TMD不合理)
// []的優先級明明比*高,那TMD就老老實實的先讀數組,然後在指針,但是好像是反過來的,刻意的記過好幾次,現在也不是很确定這個名
char* argv[] = { "ls","-l",NULL };
execvp("ls", argv);
perror("execvp(ls, argv);");
exit(1);
}
if (pid > 0)
{
sleep(1);
cout << "parent process finish" << endl;
}
return 0;
}
exec中還有一些函數就不一一介紹了,意義不大.
孤兒程序((案例)子程序循環,讓父程序先結束)
父程序先結束,子程序成為孤兒程序,子程序的父程序成為init,由init回收資源
僵屍程序((案例)父程序循環,然後子程序結束)
子程序終止,父程序沒有回收(回收PCB程序控制塊,表明死亡原因等狀态),子程序殘留,就是僵屍程序(zombie)
僵屍程序不能使用kill清除,因為僵屍程序已經終止
就是子程序結束,父程序還在運作,沒辦法回收子程序資源
wait(阻塞等待子程序退出,回收子程序殘留資源,擷取子程序結束狀态(死亡原因))
頭檔案
#include <sys/types.h>
#include <sys/wait.h>
原型
pid_t wait(int *wstatus);
傳回值
成功傳回回收子程序的ID
失敗傳回-1設定errno
wstatus是一個傳出參數
參數判斷
WIFEXITDE(wstatus) 宏函數,如果正常退出傳回真
WEXITSTATUS(wstatus) 如果傳回真,該宏函數可以擷取退出狀态(可以自行return指定)
WIFSIGNALED(wstatus) 程式異常終止
WTERMSIG(wstatus) 擷取使程序終止的信号編号
WIFSTOPPED(wstatus) 程序處于暫停狀态
WSTOPSIG(wstatus) 擷取使程序暫停的信号編号
WIFCONTINUED(wstatus) 程序暫停後已經繼續運作
案例
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, char** argv)
{
pid_t fork_pid = fork();
if (fork_pid == -1)
{
perror("fork_pid");
exit(1);
}
if (fork_pid == 0)
{
cout << "child pid = " << getpid() << endl;
exit(111);
}
if (fork_pid > 0)
{
// 函數有阻塞作用 子程序不結束父程序不會退出
int wstat;
pid_t wait_pid = wait(&wstat);
if (wait_pid == -1)
{
perror("pid_t wait_pid = wait(&wstat);");
exit(1);
}
cout << "wait_pid = " << wait_pid << ", fork_pid = " << fork_pid << endl;
// 判斷退出 程序正常退出
// 其他的宏函數我就不試了,太麻煩.
// 不過有一點需要說一下,除正常退出外,所有的狀态都是信号
if (WIFEXITED(wstat))
{
cout << "child quit number = " << WEXITSTATUS(wstat) << endl;
}
}
return 0;
}
如果不關心退出狀态可以直接給wait參數傳NULL
#include <iostream>
#include <unistd.h>
#include <wait.h>
using namespace std;
int main(int argc, char** argv)
{
pid_t fork_pid = fork();
if (fork_pid == -1)
{
perror("pid_t fork_pid = fork();");
exit(1);
}
if (fork_pid == 0)
{
cout << "fork pid = " << getpid() << endl;
}
if (fork_pid > 0)
{
pid_t wait_pid = wait(NULL);
if (wait_pid == -1)
{
perror("pid_t wait_pid = wait(NULL);");
exit(1);
}
cout << "wait_pid = " << wait_pid << endl;
}
return 0;
}
waitpid(可以回收指定程序)
頭檔案和wait一樣
原型
pid_t waitpid(pid_t pid, int *wstatus, int options);
傳回值
成功傳回回收的子程序ID
傳回0,參數三指定WNOHANG(非阻塞),并且子程序沒有退出
失敗傳回-1,設定errno
參數
pid_t
傳入-1 可以回收任意子程序(相當于,wait)
傳入0回收目前程序組的所有子程序
傳入小于-1(就是程序組前面加個負号) 回收指定程序組的所有子程序
options
0 阻塞(相當于wait)
WNOHANG 非阻塞
案例
#include <unistd.h>
#include <iostream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char** argv)
{
pid_t fork_pid;
pid_t child_pid;
for (int i = 0; i < 9; i++)
{
fork_pid = fork();
// 如果是子程序直接退出循環,不讓子程序繼續建立子程序
if (fork_pid == 0)
{
break;
}
// 拿到一個子程序的pid
if (i == 7)
{
child_pid = fork_pid;
cout << "待删除的子程序pid = " << child_pid << endl;
}
}
// 阻塞
//if (fork_pid > 0)
//{
// pid_t wait_pid = waitpid(child_pid, NULL, 0);
// cout << "回收程序的pid = " << wait_pid << endl;
//}
// 非阻塞
if (fork_pid > 0)
{
sleep(2);
pid_t wait_pid = waitpid(child_pid, NULL, WNOHANG);
cout << "回收程序的pid = " << wait_pid << endl;
}
return 0;
}
wait和waitpid每調用一次回收一個程序,回收多個需要調用多次
回收全部子程序
#include <unistd.h>
#include <iostream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char** argv)
{
pid_t fork_pid;
for (int i = 0; i < 3; i++)
{
fork_pid = fork();
if (fork_pid == 0)
{
break;
}
// 顯示子程序pid
cout << "子程序 " << i + 1 << " = " << fork_pid << endl;
}
if (fork_pid > 0)
{
pid_t parent_pid = getpid();
while (true)
{
//因為死循環,阻塞不阻塞效果相同
//
//pid_t wait_pid = waitpid(-1, NULL, 0); // -1回收任意子程序
//pid_t wait_pid = waitpid(0, NULL, 0); // 0回收目前組所有子程序,因為當下沒有其他的組是以效果一樣
pid_t wait_pid = waitpid(-parent_pid, NULL, 0); // 小于-1,父程序就是目前組,回收組下所有子程序,效果也是一樣的
if (wait_pid == -1)
{
break;
}
cout << "回收程序 " << wait_pid << endl;
}
}
return 0;
}
程序間通信IPC
常用程序通信有:命名管道(最簡單(隻能在血緣關系間)), 信号(最省資源(最快,資訊攜帶小)), 共享映射區(無血緣), 本地套接字(最穩定,最複雜)
管道
一個僞檔案(并不在磁盤上在記憶體緩沖區中)
一端寫,一端讀
實作原理:核心使用環形隊列機制,借助核心緩沖區實作大小為4K
局限性
資料不能一端讀寫
資料不能反複讀取
資料隻能單方面流動
隻能在有血緣的關系中使用
管道的讀寫行為
讀管道
有資料read讀到實際位元組數
無資料
需要判斷寫端是否關閉,read會傳回0(讀到檔案結尾傳回0)會造成起義
寫端沒有關閉,read阻塞(等待資料,阻塞時會讓出CPU)
寫管道
管道讀端全部關閉,程序異常終止(可以捕捉SIFPIPE信号,使程序不終止)
管道讀端沒有關閉
管道已滿,write阻塞(這個現在系統會自動擴容,多少不知道)
管道未滿,write傳回寫入位元組數
pipe(建立并打開管道)
頭檔案
#include <unistd.h>
原型
int pipe(int pipefd[2]);
傳回值
成功傳回0
失敗傳回-1設定errno
參數
參數, 0 讀, 1 寫
它也是一個檔案描述符,使用一端另一端可以用close(pipefd[])關閉
案例
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>
using namespace std;
int main(int argc, char** argv)
{
int pipefd[2];
int ret = pipe(pipefd);
if (ret == -1)
{
perror("int ret = pipe(pipefd);");
exit(1);
}
pid_t pid = fork();
if (pid == -1)
{
perror("pid_t pid = fork();");
exit(1);
}
if (pid > 0)
{
// 關閉管道讀端
close(pipefd[0]);
write(pipefd[1], "asdasd", 6);
// 防止父程序先退出
wait(NULL);
// 關閉寫端
close(pipefd[1]);
}
if (pid == 0)
{
// 關閉寫端
close(pipefd[1]);
char buf[111];
read(pipefd[0], buf, sizeof(buf));
cout << buf << endl;
// 關閉讀端
close(pipefd[0]);
}
return 0;
}