UNIX进程控制

8– 进程控制
本章介绍了UNIX系统的进程控制 : 创建新进程 ,执行程序和进程终止 ,还说明了进程属性的各种ID .


8.2进程标识 :
每个进程都有一个非负整形标识唯一进程ID .
常用其作为其他标识符的一部分以保证其唯一性 .
例如 :应用程序有时就把进程ID作为名字的一部分来创建一个唯一的文件 .


一些专用进程 :ID为0的进程通常是调度进程 .
              ID为1的通常是init进程 ,此进程负责在自举内核后启动一个UNIX系统 ,init通常读取与系统有关的初始化文件 .Init进程绝不会终止 .


8.3fork :
由fork创建的新进程被称为子进程 :被调用一次返回两次 :子进程 :0 父进程 :子进程ID;
一个进程只会有一个父进程 ,所以子进程总是可以调用getppid以获得其父进程的进程ID .


Fork之后,子进程是父进程的副本,例如 :子进程获得父进程的数据空间 ,堆和栈的副本 .这是子进程所拥有的副本 ,父子进程并不共享这些存储空间部分 . 父进程和子进程共享正文段 .

例程 :

/*****************************************************
*fork函数   pid_t fork(void)  用一个现有进程创建新进程
*新进程 被称为 子进程 
*子进程是父 进程 的副本  子进程 会获得 父进程的 程序数
*据空间  堆和栈的 副本    但 父进程和 子进程 共享正文段
*
*
*******************************************************/

#include<apue.h>
#include<myerr.h>

int glob_var =66;

char buf [] =  "a write to stdout\n";    


int main()
{
	int var = 6;
	
	pid_t pid;
	
	if(write(STDOUT_FILENO,buf,sizeof(buf)-1) != sizeof(buf)-1) err_sys("write error");
					       /*sizeof(宏定义) 计算包括NULL在内的字符串长度
						*父进程和子进程 共享一个 文件偏移量
						*此处 父进程等待子进程时 子进程 写到标准输出
						*而在 子进程终止后 父进程也写到标准输出 
						*/	
	
	printf("before fork\n");		
						/*我们不冲洗缓冲区
						*如果标准输出连接到终端设备 则它是行缓冲 出现一次(父进程调用后马上冲了)
						*如果重定向到文件  输出 两次  因为当调用fork 时
						*该 缓冲数据人 也被复制进 子进程 exit 之前 第二个 pri
						*ntf  将数据追加至已有的缓冲区中。
						*/
	if((pid = fork())<0) err_sys("fork error");
	
	if(pid == 0)				//子进程
	{
		glob_var++;
		var++;		
	}
	else sleep(2);		//让子进程先 运行 。  父子进程运行顺序不确定  取决于内核调度算法
	
	//printf("after fork\n");		//若放在fork  后出现 两次 。
	
	printf("pid is %d     var is %d     glob_var is %d\n",getpid(),var,glob_var);
	
	exit(0);	
}



8.4vfork
Vfork函数用于创建一个新进程 ,而该新进程的目的是exec一个新程序 .它并不将父进程的地址空间完全复制到子进程中 ,因为子进程会立即调用exec .
Vfork和fork的一个区别是 :vfork保证子进程先运行(这段时间内核会使父进程休眠) ,它在调用exec或exit之后父进程才可能被调度运行 .

/*************************************************************
*vfork  创建一个新进程 ,而该新进程 的目的是 exec 一个新程序
*它不将  父进程 空间完全 复制到子进程 因为 自己成会立刻调用 exec 或exit 
*
*vfork  保证 子进程 先运行 ,它调用exec 或exit 之后 父进程 才可能被调度运行
*************************************************************/

#include <apue.h>
#include<myerr.h>

int glob_var =66;

int main()
{
	int var = 6;
	
	pid_t pid;
	
	printf("before fork\n");/*
							 *此处若重定向普通文件也不会 出现两次了
							 */
					
	if((pid = vfork())<0) err_sys("vfork error");
	
	if(pid == 0)				//子进程
	{
		glob_var++;
		var++;	
		
		_exit(0);			//不冲洗缓冲区	
	}

	printf("pid is %d     var is %d     glob_var is %d\n",getpid(),var,glob_var);
							/*
							 *会发现 子进程 改变了父进程的 变量值 
							 *因为 子进程在 父进程 地址空间运行。
							 */
	exit(0);	
}




8.5exit


8.6 wait 和 waitpid
一个进程正常或异常终止时 ,内核就向其父进程发送SIGCHILD信号 .因为子进程终止是个异步事件 ,所以这种信号也是内核向父进程发的异步通知 .
调用wait和waitpid会发生什么 ;
1. 如果所有子进程都还在运行 ,则阻塞 .
2. 若一个子进程已经终止 ,正等待父进程获取其终止状态 ,则取得该子进程的终止状态立即返回 .

3. 如果没有任何子进程 ,则立即出错返回 .


/*******************************************************************************
*wait/waitpid  
*1.若所有进程还在运行 ,在阻塞 
*2.若一个子进程终止,正等待父进程获取其终止状态,则取得该子进程终止状态 立即返回
*3.若没任何子进程,则立即出错返回
*
*两者区别 :
*1.(waitepid 有一选项可使调用者不阻塞)
*2.waitpid 不等其调用之后的第一个终止子进程  它有若干选项可以控制它所有等待进程、
********************************************************************************/

#include<apue.h>
#include<sys/wait.h>
#include<myerr.h>

#define WCOREDUMP

void pre_exit(int status)		//打印exit状态说明
{
	
	if(WIFEXITED(status))	printf("nomal termination ,exit staus = %d\n",WEXITSTATUS(status));
	else if(WIFSIGNALED(status)) printf("abnormal termination , signal number = %d%s\n",WTERMSIG(status),
	#ifdef WCOREDUMP 
		WCOREDUMP(status) ? "(core file generated)" : "");
	#else
		"");
	#endif
		else if(WIFSTOPPED(status)) printf("child stopped, signal number = %d\n,",WSTOPSIG(status));
	
}

int 
main(void)
{
	pid_t pid;
	int status;
	
	if((pid = fork())<0) err_sys("fork error");	
		else if(pid==0) exit(7);	//WEXITSTATUS 获取子进程传送给exit参数的低8位
			
	if(wait(&status)!=pid)
		err_sys("wait error");
	else pre_exit(status);	
	
	
	
	
	
	if((pid = fork())<0) err_sys("fork error");	
		else if(pid==0) abort();	//产生异常信号
			
	if(wait(&status)!=pid)
		err_sys("wait error");
	else pre_exit(status);	
	
	
	
	
	
	if((pid = fork())<0) err_sys("fork error");	
	else if(pid==0) status/=0;	//除0产生 SIGFPE
			
	if(wait(&status)!=pid)
		err_sys("wait error");
	else pre_exit(status);	
	
	exit(0);
}


/****************************************************************
*waitpid(pid_t pid ,int *statloc, int option)
*pid ==-1 等待任一子进程 等效于 wait 
*pid >0   等待进程id 与 pid 相等的子进程
*pid ==0  等待组 ID 等于调用进程组ID的任一进程
*pid <-1  等待组 ID 等于pid 绝对值的任一进程
*
*waitpid 返回终止子进程ID , 并将其终止状态 存放statloc 所指存储单元
*
*options: 进一步的控制操作 。详 见书193
****************************************************************/


#include <myerr.h>
#include <sys/wait.h>


int
main(void)
{
	
	pid_t pid;
	
	printf("1 pid = %ld \n",(long)getpid());
	
	if((pid = fork())<0)	err_sys("fork error");
		
	else if(pid==0)	
	{
		printf("2 pid = %ld \n",(long)getpid());
		
		if((pid=fork())<0)  err_sys("fork_2 error");
		
		if(pid>0)  	//子进程fork 后 pid  为其子进程 ID 故>0  终止它 。 
		exit(0); 
		else 
		{
		
		sleep(2);	//休眠2秒确保 其 父进程 终止 否则 可能返回 其父进程 ID  而不是 init 的ID 1	
		
		printf("second child, parent pid = %ld \n",(long)getppid());
		}
		
	}
	
	if(waitpid(pid,NULL,0)!=pid) err_sys("waitpid error");
	
	exit(0);	//注意 : 子进程的 子进程  会休眠两秒 故本程序进程会先终止 返回到 shell
}




8.9竞争条件 :
当多个进程都企图对共享数据进行某种处理 ,而最后结果又取决于进程运行顺序时 我们认为发生了竞争条件 .


8.10exec

当进程调用一种 exec函数时 ,该进程执行的程序完全替换为新程序 ,而新程序则从其main函数开始执行 .因为exec并不创建新进程 ,所以前后的进程ID并未改变 ,exec只是用磁盘上的一个新程序替换了当前进程的正文段 ,数据段 ,堆栈段 .


#include <myerr.h>
#include <sys/wait.h>

char *env_init[] = {"USER=unknown","PATH=/tmp",NULL};

int main(void)
{
	pid_t pid;
	
	if((pid=fork())<0) err_sys("fork error");
	else if(pid == 0)  
	{
		
	if(execle("./echoall","echoall","myarg1","myarg2",(char *)0,env_init )<0) err_sys("execle error");	
		
	}
	
	
	
	if((waitpid(pid,NULL,0))<0) err_sys("waitpid error");
	
	
	
	if((pid=fork())<0) err_sys("fork error");	
	else if(pid == 0)  
	{
		
	if(execlp("echoall","echoall","only 1 arg",(char *)0)<0) err_sys("execle error");	
		
	}
	
	
	exit(0);
}

static void chartime(char *str) 
{
	char *ptr;
	
	int c;
	
	setbuf(stdout,NULL);	     //NULL 关闭缓冲   使内核竟可能多次的在两个进程间进行切换。
	
	for(ptr = str; (c= *ptr)!=0;ptr++)
	{
		putc(c,stdout);
	}
	
}


8.11更改用户ID和组ID .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值