unix-环境高级编程-读书笔记与习题解答-第三篇

第一章 第五节

进程与控制

该小节比较粗略的阐述了unix操作系统中用于进程控制的三个函数,分别为 : fork , exec, waitpid, 其中 exec 有五种变体, 这些变体实现的功能全部相同,只是参数不相同而已,他们统称为__exec__函数。

API

#include<unistd.h>
#include<sys/types.h>
pid_t fork(void);

pid是一个宏定义,根据平台不同而不同,大多数情况都是一个int,该函数的难点在与对进程的理解,因为该函数会创建一个进程,创建进程以后,当前的调用进程会“分叉”, 所以该函数会在父进程与子进程中分别返回不同的值, fork 成功之后会获得父进程空间的数据,堆栈资源的副本,就是深拷贝的数据副本,2个进程同时独自享有这些资源,在fork成功之后,我们无法得知子进程与父进程的运行的先后顺序,这依赖于具体实现,虽然进程创建成功之后有了2个“同时”运行的进程, 但是我们的代码是只有一份(其实是2份,但是运行的效果是相同的,无法分别),所以就出现了这种情况:

if(pid<0)
{
	fprintf(stderr,"错误!");
}
elseif(pid==0)
{
	printf(" child running");
	exit(0);
}
else
{
printf("father running and child is : %d\n",pid);
}

我们在程序中可以判断到底是哪个进程在运行,同时根据不同的进程采取不同的运行路径,为什么一段代码可以被2个进程执行呢,因为2个进程拥有相同的资源。即上面的代码其实是2个进程同时运行的(同时运行相同的代码,不是真的2个进程在运行同一段代码),只是看起来像是只有一个进程而已。

exec 系列函数

<unistd.h>
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 execve(const char *path, char *const argv[], char *const envp[]);

其中只有execve是真正的api,其他的函数都是对此函数的封装,为了实现方便快捷的调用而已。该函数首先查找要执行的文件,第一个参数可以是文件路径和文件名,也可以只是文件名。函数不同,参数不同,不过大致都是可执行文件的位置,其中第二个参数必须是可执行文件的 文件名, 可以存在于arg[0], 或者是arg0 , 总之都要是文件名, 往后的参数都是该命令的参数,该函数参数列表的结尾部分 必须是NULL, 不管是字符指针数组还是单独的若干个字符数组,其最后的一项必须为NULL。
该函数会替换当前正在执行进程本身,意思就是说当该函数执行成功,当前进程就会变成该函数启动的进程,并且调用进程会停止运行,当exec的进程执行完毕之后,调用进程也不会继续执行,因为它不是被挂起了,而是被替换掉了。
#include<sys/types.h>

#include<sys/wait.h>
pid_t waitpid(pid_t pid,int * status,int options);

该函数用来监控进程状态,第一个参数是目标进程pid,第二个参数是进程状态返回地址,可选参数提供了多种可选方案。
参数pid也可以有别的可选值:

pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于 wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为 pid 的子进程。

可选参数含义如下,摘选自百度百科

WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。

WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。

WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。

子进程的结束状态返回后存于 status,下面有几个宏可判别结束情况:

WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。

WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。

WIFSIGNALED(status)若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。

WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。

WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号。

WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。

如果执行成功返回子进程pid, 失败则返回-1, 错误识别码在 errno 中。

该节的例子程序

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>	
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAXLINE 255

char ** string_split(char*, char*);
int parse_cmd(char*);

char **
string_split(char  * str, char * split)
{
		char ** ret_split = (char **)malloc(sizeof(char * ) * MAXLINE);
		char * temp;
		temp = strtok(str, split);
		int i = 0;
		while(temp)
		{
    		*(ret_split + i) = temp;
    		i++;
    		temp = strtok(NULL, split);
		}
		*(ret_split +i) = NULL; 

		return ret_split;
}

int 
parse_cmd(char * cmd)
{
		char ** cmds = string_split(cmd, " ");    
		int signal = execvp(*(cmds + 0), cmds); 
		return signal;
}

int 
main(void)
{
		char buf[MAXLINE];
		pid_t pid;
		int status;

		printf("%%");
 	   while(fgets(buf, MAXLINE, stdin) != NULL)
	{
    	buf[strlen(buf) - 1] = 0;
    	if((pid = fork()) < 0)
    	{
        	printf("fork error");
    	}
    	else if(pid == 0)
    	{  
        		parse_cmd(buf);
        		printf("could not execute : %s\n", buf);
        		exit(127);
    	}
    	if((pid = waitpid(pid, &status, 0)) < 0)
    	{
        		printf("waitpid error");
    	}
    	printf("%d\n", status);
    	printf("%%");

	}

 exit(0);

}

该程序是书中程序的变种,书中的程序是不能带参数执行命令的,我这里简单的处理了一下参数,这是可独立变异运行的版本,不需要ourhdr.h 头文件的支持。

posted @ 2014-10-03 00:37  C-Dog  阅读(325)  评论(0编辑  收藏  举报