天天看點

Linux系統程式設計-程序二EXEC函數族孤兒程序((案例)子程序循環,讓父程序先結束)僵屍程序((案例)父程序循環,然後子程序結束)wait(阻塞等待子程序退出,回收子程序殘留資源,擷取子程序結束狀态(死亡原因))waitpid(可以回收指定程序)回收全部子程序 程序間通信IPCpipe(建立并打開管道)

感覺需要說一下,概念性的東西,都隻是個人了解.

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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

 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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

 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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

 如果不關心退出狀态可以直接給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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

 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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)

 程序間通信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;
}
           
Linux系統程式設計-程式二EXEC函數族孤兒程式((案例)子程式循環,讓父程式先結束)僵屍程式((案例)父程式循環,然後子程式結束)wait(阻塞等待子程式退出,回收子程式殘留資源,擷取子程式結束狀态(死亡原因))waitpid(可以回收指定程式)回收全部子程式 程式間通信IPCpipe(建立并打開管道)