管道与信号

来源:互联网 发布:微信拼图游戏源码 编辑:程序博客网 时间:2024/06/05 10:50

一、管道的概念

管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。

1、无名管道

pipe

函数原型:int   pipe(int   filedis[2]);

函数参数:新建的两个描述符filedis数组返回,filedis[0]表示管道的读取端,fds[1]表示管道的写入端

返回值:成功返回0,出错返回-1

头文件:#include<unistd.h>

#include <unistd.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>int main(){int pipe_fd[2];if(pipe(pipe_fd)<0){printf("pipe create error\n");return -1;}else printf("pipe create success\n");close(pipe_fd[0]);close(pipe_fd[1]);}

2、管道读写

管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道

 

#include <unistd.h>#include <sys/types.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>int main(){int pipe_fd[2];pid_t pid;char buf_r[100];char* p_wbuf;int r_num;memset(buf_r,0,sizeof(buf_r));/*创建管道*/if(pipe(pipe_fd)<0){printf("pipe create error\n");return -1;}/*创建子进程*/if((pid=fork())==0)  //子进程 OR 父进程?{printf("\n");close(pipe_fd[1]);sleep(2); /*为什么要睡眠*/if((r_num=read(pipe_fd[0],buf_r,100))>0){printf(   "%d numbers read from the pipe is %s\n",r_num,buf_r);}close(pipe_fd[0]);exit(0);  }else if(pid>0){close(pipe_fd[0]);if(write(pipe_fd[1],"Hello",5)!=-1)printf("parent write1 Hello!\n");if(write(pipe_fd[1]," Pipe",5)!=-1)printf("parent write2 Pipe!\n");close(pipe_fd[1]);sleep(3);waitpid(pid,NULL,0); /*等待子进程结束*/exit(0);}return 0;}

3、有名管道通信

有名管道无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;

但是通过命名管道,不相关的进程也能交换数据。

函数原型:int  mkfifo(const char  *filename,mode_t  mode)

函数作用:创建有名管道

函数参数:filename:有名管道的路径,名称

           mode:管道的方式

几种方式:O_NONBLOCK    FIFO打开的时候,非阻塞

           O_RDONLY      只读

           O_WRONLY      只写

           O_RDWR        读写

返回值:成功为0,出错为-1

    一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(closereadwrite等)都可用于FIFO

注意:

FIFO文件在使用上和普通文件有相似之处,但是也有不同之处:

1读取fifo文件的进程只能以”RDONLY”方式打开fifo文件。

2)写fifo文件的进程只能以”WRONLY”方式打开fifo

3 fifo文件里面的内容被读取后,就消失了。但是普通文件里面的内容读取后还存在。

4、有名管道的读写

#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define FIFO "/tmp/myfifo"main(int argc,char** argv){char buf_r[100];int  fd;int  nread;/* 创建管道 */if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))printf("cannot create fifoserver\n");printf("Preparing for reading bytes...\n");memset(buf_r,0,sizeof(buf_r));/* 打开管道 */fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);if(fd==-1){perror("open");exit(1);}while(1){memset(buf_r,0,sizeof(buf_r));if((nread=read(fd,buf_r,100))==-1){if(errno==EAGAIN)printf("no data yet\n");}printf("read %s from FIFO\n",buf_r);sleep(1);}pause(); /*暂停,等待信号*/unlink(FIFO); //删除文件}
#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define FIFO_SERVER "/tmp/myfifo"main(int argc,char** argv){int fd;char w_buf[100];int nwrite;/*打开管道*/fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);if(argc==1){printf("Please send something\n");exit(-1);}strcpy(w_buf,argv[1]);/* 向管道写入数据 */if((nwrite=write(fd,w_buf,100))==-1){printf("The FIFO has not been read yet.Please try later\n");}else printf("write %s to the FIFO\n",w_buf);}


二、信号

1、信号(signal)机制Unix系统中最为古老的进程间通信机制,很多条件可以产生一个信号(信号分类):

1)、当用户按某些按键时,产生信号

2)、硬件异常产生信号:除数为0、无效的存储访问等等。这些情况通常由硬件检测到,将其通知内核,然后内核产生适当的信号通知进程,例如,内核对正访问一个无效存储区的进程产生一个SIGSEGV信号

3)、进程用kill函数将信号发送给另一个进程

4)、用户可用kill命令将信号发送给其他进程

信号处理流程图如下:

下面是几种常见的信号类型:

§SIGHUP从终端上发出的结束信号

§SIGINT来自键盘的中断信号(Ctrl-C

§SIGKILL:该信号结束接收信号的进程,杀死进程

§ SIGTERMkill命令发出的信号

§SIGCHLD:子进程停止或结束时通知父进程

§SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号,暂停进程

2、信号处理

当某信号出现时,将按照下列三种方式中

的一种进行处理:

1)、忽略此信号

 大多数信号都按照这种方式进行处理,但有两种信号决不能被忽略,它们是:SIGKILL\SIGSTOP这两种信号不能被忽略的原因是:它们向超级用户提供了一种终止或停止进程的方法。

2)、执行用户希望的动作

         通知内核在某种信号发生时,调用一个用户函数。在用户函数中,执行用户希望的处理

3)、执行系统默认动作

         对大多数信号的系统默认动作是终止该进程

3、信号发送

发送信号的主要函数有killraise

信号发送:kill

函数的作用:传送信号给指定的进程

函数的原型:int  kill(pid_t  pid,int  sig)

函数的参数:(1)pid>0

             将信号发送给进程IDpid的进程。

(2)pid == 0

             将信号发送给同组的进程。

(3)pid < 0

          将信号发送给其进程组ID等于pid绝对值的进程。

(4)pid ==1

                    将信号发送给所有进程。

#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <sys/types.h>#include <sys/wait.h>int main(){   pid_t  pid;   int ret;      if((pid=fork()) < 0)   {      perror("fork");      exit(1);   }      if(pid == 0)   {   raise(SIGSTOP);   exit(0);   }   else   {     printf("pid=%d\n", pid);     if((waitpid(pid, NULL, WNOHANG)) == 0)     {         kill(pid,SIGKILL);         printf("kill %d\n", pid);    }     else     {        perror("kill");     }              }}

raise

函数的功能:发送信号给自身

函数的原型:int  raise(int   sig)

头文件:#include <signal.h>

#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <signal.h>int main(){   pid_t pid;   int ret;      if((pid= fork()) < 0)   {       printf("Fork error.\n");       exit(-1);   }   if(pid == 0)   {       printf("child (pid:%d) is waiting for any signal\n.", getpid());       raise(SIGSTOP);       exit(0);   }   else   {   if((waitpid(pid, NULL, WNOHANG)) == 0)   {      kill(pid, SIGKILL);      printf("parent kill child process %d\n", pid);         }   waitpid(pid, NULL, 0);   exit(0);   }}

Kill既可以向自身发送信号,也可以向其他进程发送信号。与kill函数不同的是,raise函数是向进程自身发送信号.

alarm

函数原型:unsigned intalarm(unsigned int seconds)

头文件:#include<unistd.h>

参数说明:Seconds 

          经过了指定的seconds秒后会产生信号SIGALRM

v  注意:每个进程只能有一个闹钟时间.如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,以前登记的闹钟时间则被新值代换

v  如果有以前登记的尚未超过的闹钟时间,而这次seconds值是0,则表示取消以前的闹钟

pause

函数原型:             int pause(void)

函数作用: 使调用进程挂起直至捕捉到一个信号。

4、信号处理

当系统捕捉到某个信号时,可以忽略该信号或是使用指定的处理函数来处理该信号,或者使用系统默认的方式。

信号处理的主要方法有两种,一种是使用简单的signal函数,另一种是使用信号集函数组。

signal

函数作用:信号处理函数,设置信号处理方式

函数原型:void (*signal(int signo, void (* sighandler_t)(int)))(int)

typedef void(*sighandler_t)(int) sighandler_t

signal(intsignum, sighandler_t handler))

#include <signal.h>#include <stdio.h>#include <stdlib.h>/*自定义信号处理函数*/void my_func(int sign_no){if(sign_no==SIGBUS)printf("I have get SIGBUS\n");}int main(){printf("Waiting for signal SIGBUS \n ");/*注册信号处理函数*/signal(SIGBUS,my_func);pause();//将进程挂起直到捕捉到信号为止exit(0);}

0 0