【系统编程】 进程间通信方式

1.管道pipe:

  利用管道进行父子进程间通信:

  

   利用管道进行父子间双向通信:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#define BUF_SIZE 20
int main(int argc,char *argv[]){
    int fd[2],fd1[2];
    char buf[BUF_SIZE];
    pipe(fd);pipe(fd1);
    pid_t pid = fork();
    if(pid==0){
        char str[] = "Hello world\n";
        write(fd[1],str,sizeof(str));
        int len = read(fd1[0],buf,BUF_SIZE);
        write(STDOUT_FILENO,buf,len);
    }   
    else{
        sleep(1);
        char str[] = "zytxdy\n";
        write(fd1[1],str,sizeof(str));
        int len = read(fd[0],buf,BUF_SIZE);
        write(STDOUT_FILENO,buf,len);
    }   
    sleep(1);
    return 0;
}
View Code

2.有名管道FIFO:
创建方法:1.直接mkfifo  FIFONAME 创建有名管道。

      

     2.在.c里写代码创建,头文件包括sys/stat.h,参数1是名称,参数2是权限  

                     

 实现无血缘间进程的通信:

  其实FIFO和文件打开关闭相似性大,与管道pipe其实关联性并不大,FIFO可以一个写端多个读端/多个写端一个读端。

  /*注意以下代码运行的时候要加参数,参数即为FIFO名*/

  写端write:

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

int main(int argc,char *argv[]){
    int fd,i;
    char buf[4096];

    fd = open(argv[1],O_WRONLY);
    i=0;
    while(1){
        sprintf(buf,"hello itcast: %d\n",i++);

        write(fd,buf,strlen(buf));
        sleep(1);
    }
    close(fd);
    return 0;
}
View Code

  读端read:

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

int main(int argc,char *argv[]){
    int fd,len;
    char buf[4096];

    fd = open(argv[1],O_RDONLY);
    int i=0;
    while(1){
        len = read(fd,buf,sizeof(buf));
        write(STDOUT_FILENO,buf,len);
        sleep(1);
    }
    close(fd);
    return 0;
}
View Code

3.共享存储映射:

  1.文件直接用于通信(由于比较简单就略过)

  2.存储映射I/O(mmap:Memory map):

  

  

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<error.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>
int main(int argc,char *argv[]){
    char *p = NULL;
    int fd;
    fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);
    
    lseek(fd,20,SEEK_END);   //扩展文件大小
    write(fd,"\0",1);

    int len = lseek(fd,0,SEEK_END); //获取文件大小
   
    p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //可读可写,p为字符串形式文件内容
    if(p == MAP_FAILED){
        perror("mmap error");
        exit(1);
    }
    //使用 p 对文件进行读写操作
    strcpy(p,"Hello mmap"); //写操作

    printf("-----%s\n",p);

    int ret = munmap(p,len);
    if(ret == -1){
        perror("munmap error");
        exit(1);
    }
    return 0;
}
View Code

 

4.信号集操作函数:

   

  set会与阻塞信号集做或操作

  

  

    

   示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<errno.h>
void print_set(sigset_t *set){
    for(int i=1; i<32 ;i++){
        if(sigismember(set,i))
            putchar('1');
        else
            putchar('0');
    }
    printf("\n");
}

int main(int argc,char *argv[]){
    sigset_t set,oldset,pedset;
    int ret = 0;

    sigemptyset(&set);
    sigaddset(&set,SIGINT);

    ret = sigprocmask(SIG_BLOCK,&set,&oldset);
    if(ret == -1){
        perror("sigprocmask error");
        exit(1);
    }

    while(1){
        ret = sigpending(&pedset);
        if(ret == -1){
            perror("sigpending error");
            exit(1);
        }

        print_set(&pedset);
        sleep(1);
    }
    return 0;
}
View Code

   如下所示,按下<ctrl+c>组合键就能看到位发生了变化,因为其在未决信号集之中

   

 ps:注意,kill -9即使被屏蔽了也照样生效,其他的被屏蔽了就无法生效。

5.信号:

   阻塞信号集(信号屏蔽字):将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)

   未决信号集:

    1.信号产生,未决信号集中描述该信号的位立刻为1,表示信号处于未决状态。当信号被处理对应位反转为0。这一时刻往往非常短暂。

    2.信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称为未决信号集。在屏蔽解除前,信号一直处于未决状态。

  信号4要素:

    信号编号、信号名称、信号对应事件、信号默认处理动作    

    信号使用之前,应先确定其4要素,而后再用。

      

    默认动作:

      Term:终止进程

      lgn:忽略信号(默认即时对该种信号忽略操作)

      Core:终止进程,生成Core文件。(查验进程死亡原因,用于gdb调试)

      Stop:停止(暂停)进程

      Cont:继续运行进程

   硬件异常产生信号:

    除0操作 -> 8)SIGFPE(浮点数例外)

    非法访问内存 -> 11)SIGSEGV(段错误)

    总线错误  -> 7)SIGBUS

信号捕捉:

  signal函数:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_catch(int signo){
    printf("catch you! %d\n",signo);
    return ;     
}
int main(int argc,char *argv[] ){
     signal(SIGINT,sig_catch);        
     while(1);  
}
View Code

      这时候编译运行后,一旦输入<ctrl+c>组合键,就会显示捕捉到信号  

  

   sigaction函数:

  也是用来注册一个信号的捕捉函数

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_catch(int signo){        //步骤函数,回调函数
    printf("catch you! %d\n",signo);
    return ;     
}
int main(int argc,char *argv[] ){
     struct sigaction act,oldact;

     act.sa_handler = sig_catch; //设置捕捉的函数名
     sigemptyset(&act.sa_mask);//初始化,设置屏蔽字mask
     act.sa_flags = 0;                 //默认属性

     sigaction(SIGINT,&act,&oldact);        
     while(1);  
}
View Code

  当然我们也可以捕捉很多函数,如下图所示即可捕捉2个信号:

  

   信号捕捉特性:

    1:捕捉函数执行期间,信号屏蔽字,由mask -->sa_mask,捕捉函数执行结束,恢复回mask

    2:捕捉函数执行期间,本信号自动被屏蔽(sa_flags = 0)

    3:捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次

    

前ICPC算法竞赛退役选手|现摸鱼ing
原文地址:https://www.cnblogs.com/Anonytt/p/15560669.html