循序渐进学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);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值