IPC机制和实现

(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。

  (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。

  (3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。

  (4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺

   创建: msgget();写入消息:msgsnd();读取消息:msgrcv();删除:msgctl();

    (5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥

   创建:shmget();附加:shmat();分离:shmdt();

    (6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。

  (7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段

   创建:semget();控制删除:semctl();

    (8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

扫描二维码关注公众号,回复: 617793 查看本文章

 

一、管道

(1)管道

    管道是单向的、先进先出的、无结构的、固定大小的字节流它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。读写管道的例程:

#include
#include
#include
#include
#include

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)   //在子进程中读管道(关闭写管道)
 {
  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 success!\n");
  if(write(pipe_fd[1]," Pipe",5)!=-1)
   printf("parent write2 success!\n");
  close(pipe_fd[1]);
  sleep(3);
  waitpid(pid,NULL,0);  //等待子进程结束
  exit(0);
 }
}

(2)标准流管道

 #include
#include
#include
#include

#define BUFSIZE 1000

int main()
{
 FILE *fp;
 char *cmd = "ps -ef";
 char buf[BUFSIZE];
 buf[BUFSIZE] = '\0'; 
 if((fp=popen(cmd,"r"))==NULL)
  perror("popen");
 while((fgets(buf,BUFSIZE,fp))!=NULL)
  printf("%s",buf);
 pclose(fp);
 exit(0);
}

(3)命名管道

     也叫FIFO,与管道不同,FIFO不是临时的对象,它们是文件系统中真正的实体,可以用mkfifo命令创建。只要有合适的访问权限,进程就可以使用FIFOFIFO的打开方式和管道稍微不同。一个管道(它的两个file数据结构、VFS I节点和共享数据页)是一次性创建的,而FIFO已经存在,可以由它的用户打开和关闭。Linux必须处理在写进程打开FIFO之前读进程对它的打开,也必须处理在写进程写数据之前读进程对管道的读。除此以外,FIFO几乎和管道的处理完全一样,而且它们使用一样的数据结构和操作。

读进程:

#include
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#define FIFO_SERVER "/tmp/myfifo"

main(int argc,char** argv)
{
 int fd;
 char w_buf[100];
 int nwrite;
 
 if(fd==-1)
  if(errno==ENXIO)
   printf("open error; no reading process\n");
 fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
 if(argc==1)
  printf("Please send something\n");
 strcpy(w_buf,argv[1]);
 if((nwrite=write(fd,w_buf,100))==-1)
 {
  if(errno==EAGAIN)
   printf("The FIFO has not been read yet.Please try later\n");
 }
 else
  printf("write %s to the FIFO\n",w_buf);
}

二、信号

(1)信号的发出:kill(),raise()给自身进程发送信号。

#include
#include
#include
#include
#include

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){
   if((ret=kill(pid,SIGKILL))==0)
    printf("kill %d\n",pid);
   else{
    perror("kill");
   }
  }
 }
}

(2)alarm(),pause()

#include
#include
#include

int main()
{
        int ret;
        ret=alarm(5);
        pause();
        printf("I have been waken up.\n",ret);
}

(3)signal(),发送信号

ctrl+c:SIGINT,ctrl+/:SIGQUIT

#include
#include
#include
void my_func(int sign_no)
{
 if(sign_no==SIGINT)
  printf("I have get SIGINT\n");
 else if(sign_no==SIGQUIT)
  printf("I have get SIGQUIT\n");
}
int main()
{
 printf("Waiting for signal SIGINT or SIGQUIT \n ");
 signal(SIGINT, my_func);
 signal(SIGQUIT, my_func);
 pause();
 exit(0);
}

(4)信号集,接受多个信号,同时多个信号处理函数

#include
#include
#include
#include
#include

void my_func(int signum)
{
 printf("If you want to quit,please try SIGQUIT\n");
}
int main()
{
 sigset_t set,pendset;
 struct sigaction action1,action2;
 if(sigemptyset(&set)<0)
  perror("sigemptyset");
 if(sigaddset(&set,SIGQUIT)<0)
  perror("sigaddset");
 if(sigaddset(&set,SIGINT)<0)
  perror("sigaddset");
 if(sigprocmask(SIG_BLOCK,&set,NULL)<0)
  perror("sigprocmask");
 else
 {
  printf("blocked\n");
  sleep(5);
 }
 if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)
  perror("sigprocmask");
 else
  printf("unblock\n");
 while(1){
  if(sigismember(&set,SIGINT)){
   sigemptyset(&action1.sa_mask);
   action1.sa_handler=my_func;
   sigaction(SIGINT,&action1,NULL);
  }else if(sigismember(&set,SIGQUIT)){
   sigemptyset(&action2.sa_mask);
   action2.sa_handler = SIG_DFL;
   sigaction(SIGTERM,&action2,NULL);
  }
 }
}

 

猜你喜欢

转载自windshg.iteye.com/blog/1872985