1. 程式人生 > >循序漸進學unix——上機記錄(六),exec

循序漸進學unix——上機記錄(六),exec

本次上機記錄的主題是如何執行外部程式。Unix下有一組名為execxx()的函式:

#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 execv(const char *

path, char *const argv[]);
int execvp(const char *
file, char *const argv[]);
int execvpe(const char *
file, char *const argv[], char *const envp[]);


可以看出,這組函式的第一個引數是我們需要呼叫的程式檔名或詳細路徑,第二部分引數是為新程式傳遞的引數,在名為execlx的函式中,這些引數使用變長的引數列表傳遞,而在名為execvx的函式中這些引數使用一個引數陣列傳遞。注意,引數的第一個永遠是待呼叫的程式名稱,最後一個乃是NULL。例如若想在程式中執行“ls -l”, 則應該呼叫:

execl("ls", "ls", "-l", NULL); 或

execv("ls", argv), 其中argv為一個包含"ls", "-l", NULL 的字串陣列。

另外execle中的e代表environment,表示希望傳遞的環境引數。

下面來看看這次的上機題:

1. 結合pipe和exec,寫程式執行命令:"ls -l  | tail -3 | wc -w"

這道題結合了管道和程式呼叫,要求有相對細緻的理解。每當我們使用exec呼叫外部程式以後,當前程序就會被完全替換為新程序,所以我們需要3個程序分別執行ls,tail和wc命令,他們之間通過建立pipe實現資料的傳遞。


#define _GNU_SOURCE
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h> //pour le flag O_CLOEXEC

void main()
{

	int val_fork;
	int pipe12[2];
	pipe(pipe12);//, O_CLOEXEC);
	
	if( (val_fork=fork())==0)
	{//Fils1 ls
		close(pipe12[0]);
		dup2(pipe12[1], 1);
		close(pipe12[1]);
		execlp("ls", "ls", "-l", NULL);
		exit(0);
	}
	
	int pipe23[2];
	pipe(pipe23);
	//pipe2(pipe23, O_CLOEXEC);
	if( (val_fork=fork())==0)
        {//Fils2 tail
		//ls -l -> tail
		close(pipe12[1]);
		dup2(pipe12[0], 0);
		close(pipe12[0]);
		// tail -> wc
                close(pipe23[0]);
                dup2(pipe23[1], 1);
                close(pipe23[1]);

                execlp("tail", "tail", "-3", NULL);
                exit(0);
        }
	close(pipe12[0]);
	close(pipe12[1]);
	
	int pipe31[2];
	pipe(pipe31);
	//pipe2(pipe31, O_CLOEXEC);
	if( (val_fork=fork())==0)
        {//Fils3 wc
                //tail->wc
                close(pipe23[1]);
                dup2(pipe23[0], 0);
                close(pipe23[0]);
                // wc -> pere
                close(pipe31[0]);
                dup2(pipe31[1], 1);
                close(pipe31[1]);

                execlp("wc", "wc", "-w", NULL);
                exit(0);
        }
	close(pipe23[0]);
	close(pipe23[1]);

	close(pipe31[1]);
        dup2(pipe31[0], 0);
	close(pipe31[0]);

	int resultat;
	scanf("%d", &resultat);

	printf("Le résultat de la commande \"ls -l | tail -3 | wc -w\" est : %d.\n", resultat);
	exit(0);
}


有一點如果不留意的話很容易出問題:一定要立即關閉不再使用的pipe。因為一些程式,比如tail,是當在pipe中讀到EOF時才結束讀入並開始執行的,所以如果沒有及時關閉pipe會導致程式一直處於等待狀態。


2, 這道題好像更像是關於signal的,補充在這裡,權當複習吧。

建立一個子程序執行一項耗時的命令,並在5秒鐘後將其殺死。主要涉及到alarm函式的使用。這一函式有一時間引數,可以在指定時間後向自身程序傳送一個SIGALRM訊號,進而我們可以通過這一訊號的處理函式將子程序殺死。


#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

int val_fork;
void kill_fils( int signum)
{
	kill(val_fork, SIGKILL);
	printf("Je viens de tuer mon fils!\n");
}
void main()
{
	if( (val_fork=fork()) == 0 )
	{
		while(1)
		{
			printf("Je m'occupe sur un travail de durée longue!\n");
			sleep(1);
		}
	}else
	{
		signal(SIGALRM, kill_fils);
		alarm(5);
		wait(NULL);//Indispensable, sinon pb1: Père meurt, alarm() ne marche plus. pb2:On ne pourra pas terminer le fils en utilisant Ctrl-c, car le fils est détaché du terminal.
	}
}

3,寫程式證明:在執行exec函式之後,當前程序還能接受並處理之前配置過的訊號嗎?


根據exec的執行原理我們知道,呼叫這一函式後當前程序的上下文會完全被替換,所以之前所做的訊號配置也就失效了。

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

int val_fork;
void sigfunction( int signum)
{
	//kill(val_fork, SIGKILL);
	printf("Je viens de recevoir un signal!\n");
}
void main()
{
	if( (val_fork=fork()) == 0 )
	{
		signal(SIGUSR1, sigfunction);
		execlp("sleep", "sleep", "5", NULL);
	}else
	{
		sleep(2);
		kill(val_fork, SIGUSR1);
	}
}