1Linux环境下进程控制与进程间通讯1.进程的建立与运行在UNIX中,一个进程可以启动另一个,所有进程的源为init。进程控制方面的主要系统调用:1.fork调用:通过复制调用进程来建立新进程2.exec调用:通过用一个新的程序覆盖原内存空间来实现进程的转变。3.wait()调用:使一个进程等待直到另一个进程结束为止。4.exit():终止一个进程的执行。[产生进程Fork调用]#includestdio.h#includeunistd.h函数:pid_tfork()功能:创建一个新的进程.说明:本系统调用产生一个新的进程,叫子进程,是调用进程的一个复制品调用进程叫父进程,子进程继承了父进程的几乎所有的属性。返回值:调用成功则对子进程返回0,对父进程返回子进程号,这也是最方便的区分父子进程的方法.若调用失败则返回-1给父进程,子进程不生成.#includestdio.h#includeunistd.hmain(){pid_tpid;printf(“nowonlyoneprocess\n”);printf(“callforfork…\n”);pid=fork();if(!pid)printf(“Iamthechild\”);elseif(pid0)printf(“Iamtheparent.Childhaspid%d\n”,pid);elseprintf(“forkfail\n”);}程序执行的结果?[进程执行exec()调用]函数:exec()功能:执行一个文件语法:#includeunistd.hintexecl(path,arg0,...,argn,(char*)0)char*path,*arg0,...,*argn;intexecv(path,argv)char*path,*argv[];intexecle(path,arg0,...,argn,(char*)0,envp)char*path,*arg0,...,*argn,*envp[];2intexecve(path,argv,envp)char*path,*argv[],*envp[];intexecvp(file,argv)char*file,*argv[];intexeclp(arg0,...,argn,(char*)0)char*arg0,...,*argn;说明:这是一个系统调用族,用于将一个新的程序调入本进程所占的内存,并覆盖之,产生新的内存进程映象。新的程序可以是可执行文件或SHELL批命令。当C程序被执行时,是如下调用的:main(intargc,char*argv[],char*envp[]);argc是参数个数,argv是各个参数字符串指针数组,envp是新进程的环境变量字符串的指针数组。Execvp(),execlp()不指明执行文件的PATH,而用SHELL中的PATH指出目录。例如:PATH=/bin;/usr/bin;/sbin返回值:该系统调用一般不会有成功返回值,因为原来的进程已荡然无存。例子:printf(nowthisprocesswillbepscommand\n);execl(/bin/ps,ps,-ef,NULL);perror(“execlfailedtorunps”);例如:main(){intpid;pid=fork():switch(pid){case–1:perror(”forkfailed”);exit(1);case0:execl(”/bin/ls”,”ls”,”-l”,”--color”,NULL);perror(“execlfailed”);exit(1);default:wait(NULL);printf(“lscompleted“);exit(0);}}进程的终止:voidexit(intstatus);停止进程,关闭所有打开的文件,重新启动父进程,父进程可获得status的低8位。进程的同步:pid_twait(int*status)返回结束的进程的pid。Status不为null时,返回exit的出口信息。进程的属性1.进程标识符:为非负整数,在系统中唯一,进程0为调度进程,进程1为初始化进程。Pid=getpid();ppid=getppid()2.进程组标识符:进程组对于进程间的通信机构——信号有用;getpgrp();某个用户退出系统时,系统根据组标识符来选定该终止的进程。改变进程组newgrp=setpgrp();所建立的新进程将继承newgrp。这允许某些进程的生命期超出用户的注册期。3.进程环境externchar**environ;main(){char**env=environ;while(*env)printf(%s\n,*env++);3}LESSOPEN=|/usr/bin/lesspipe.sh%sHISTSIZE=1000HOSTNAME=ele.pku.edu.cnLOGNAME=liuzmREMOTEHOST=162.105.204.163MAIL=/usr/spool/mail/liuzmTERM=ansiHOSTTYPE=i386PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/liuzm/bin:.HOME=/home/liuzmINPUTRC=/etc/inputrcSHELL=/bin/bashUSER=liuzmLANG=en_USOSTYPE=LinuxSHLVL=1LS_COLORS=_=./a.out进程的当前目录:chdir();chroot()进程的资源:进程通信主要有以下几种方法:1.管道:无名、有名2.信号量3.Messagequeue队列编程;4.共享内存编程;5.TCP/IPsocket编程;1.管道pipepipe(fd)写入端fd[1]读出端fd[0]write(fd[1],buf,size)read(fd[0],buf,size)函数声明:intpipe(intfd[2]);返回值:0:成功-1:失败用系统调用pipe(fd)建立管道文件,读端fd[0],写端fd[1],管道按照FIFO方式交换信息。只能用于父子进程间。例1:父子进程间用管道方式通信。main(){intpid,fd[2];charbuf[30],s[30];pipe(fd);4pid=fork():switch(pid){case–1:perror(”forkfailed”);exit(1);case0:sprintf(buf,”Thisisanexample\n”);write(fd[1],buf,30);exit(0);default:wait(NULL);read(fd[0],s,30);printf(“%s“,s);}}例2:父进程生成P1,P2两子进程分别向管道写入各自的字符,父进程读出它们。main(){intpid1,pid2,fd[2];charbuf[50],s[50];pipe(fd);pid1=fork():switch(pid1){case–1:perror(”forkfailed”);exit(1);case0:lockf(fd[1],1,0);//锁定写入端sprintf(buf,”childp1issendingmessages!\n”);write(fd[1],buf,50);sleep(5);lockf(fd[1],0,0);//释放写入端exit(0);default:pid2=fork():switch(pid2){case–1:perror(”forkfailed”);exit(1);case0:lockf(fd[1],1,0);//锁定写入端sprintf(buf,”childp2issendingmessages!\n”);write(fd[1],buf,50);sleep(5);lockf(fd[1],0,0);//释放写入端exit(0);default:wait(NULL);read(fd[0],s,50);printf(“%s“,s);wait(NULL);read(fd[0],s,50);printf(“%s“,s);exit(0);}}问题:如果没有lockf(),结果会怎样?注意:(1)pipe()的调用必须在fork()之前(2)及时关闭不需要的句柄(3)管道通信只能用于父子进程间。2.有名管道有名管道的创建:5#mknodsampleFIFO或#mkfifo-m0666sampleFIFO例子:#defineFIFO-FILE“sampleFIFO“voidmain(void){FILE*fp;charreadbuf[80];mknod(FIFO-FILE,S_IFIFO|0666,0);while(1){fp=fopen(FIFO-FILE,”r”);fgets(readbuf,80,fp);printf(“Receivedstring:%s\n”,readbuf);fclose(fp);}}#fileserver&有名管道有自动阻塞功能#defineFIFO-FILE“sampleFIFO“main(intargc,char*argv[]){FILE*fp;if(argc!=2){printf(”Usage:fifoclient[string]\n”);exit(1);}if(fp=fopen(FIFO-FILE,”w”)==NULL){perror(“fopen”);exit(1);}fputs(argv[1],fp);fclose(fp);}信号:信号的种类:SIGHUP:终端终止时,系统向同组进程发送的信号SIGINT:在终端上按(Ctrl+C)键时,系统向同组进程发送的信号SIGKILL:进程间发送的信号SIGALRM:定时器到时,内核向进程发送的信号,定时器由alarm()设定。SIGCHLD:子进程结束信号。子进程调用exit()时,父进程正在执行wait()时可获得该信号。处理信号的机制:信号的处理intsignal(intsig,_sighandler_thandler);6参数1:sig指明要处理的信号类型,参数2:(1)handler为返回值为int的函数地址,当接收到要处理的信号时,执行由handler指定的函数,函数的形式为intfunc(intsig)(2)SIG_IGN,忽略类型sig的信号(3)SIG_DFL,恢复系统对信号sig的默认处理在进程之间发送信号intkill(pid_tpid,intsig)由pid指定信号发送的对象:0:发送给进程同组的所有进程-1:发送给全部进程-1:发送给由-pid指定的进程alarm()和pause()unsignedintalarm(unsignedintseconds);启动定时:alarm(n);终止定时:alarm(0);进程执行exec()后,定时仍然有效。在fork()后,在子进程中无效。pause()使调用进程暂停,直到接收到某种信号为止。#defineTRUE1#defineFALSE0#defineBELLS“\007\007\007”intalarm_flag=FALSE;setflag(){alarm_flag=TRUE;}main(intargc,char*argv[]){intnsecs,i;if(argc2){fprintf(steder,”Usage:tml#minutesmessage\n”);exit(1);}if((nsecs=atoi(argv[1]*60)=0){fprintf(steder,”Invalidtime\n”);exit(2);}signal(SIGALRM,setflag);alarm(nsects);pause();if(alarm_flag){printf(BELLS);for(i=2;iargc;i++)printf(“%s\n”,argv[i]);}exit(0);}