进程之间的通信之早期通信

前言:

1、进程间的通信分为三类:

早期:无名管道(pipe)、有名管道(fifo)和信号(signal)

系统5 IPC对象:共享内存(share memory)、消息队列(message queue)和信号灯(semaphore)

网路:套接字(socket)

2、管道:

管道是Linux中 进程通信的一种方式,他把一个程序的输出,直接接到另外一个程序的输入中,Linux的管道通信主要分为两种,有名管道,无名管道,管道文件表示符 TT。

一、无名管道

1、无名管道的介绍:

无名管道是Linux通信的一种原始方式,具有以下特点:

(1)只能在具有亲缘关系的进程之间通信(父子进程或者兄弟进程)

(2)是单工的(一端收,一端发),具有固定的读端和写端

(3)管道也可以看成一种特殊的文件,对于他的读写也可以使用他的read(),write()等函数,但他不属于任何系统文件,是只存在于内存当中的。

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

模型:

pipe函数:

 pipe:创建无名管道
      #include <unistd.h>
        int pipe(int pipefd[2]);
        功能: 
            创建无名管道 
                     
        参数: 
            pipefd[2]:整型数组
               存放两个文件描述符:一个负责读 一个负责写 
                        [0]--->读取 
                        [1]--->写入 
                                        
          返回值: 
               成功返回0 
               失败返回-1,并设置错误码

2、测试代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
    //制作无名管道需要用到整型数组 ,一个写,一个读
    int pipefd[2];
    
    //创建无名管道 ,返回值为0则创建成功
    int ret=pipe(pipefd);
    if(ret<0){
        perror("pipe");
        return -1;
    }
    
    printf("[0]:%d\n",pipefd[0]);
    printf("[1]:%d\n",pipefd[1]);
    
    //开出父子进程,父子进程依赖无名管道进行通信 
    pid_t pid=fork();//开出父子进程 
    if(pid>0){
       //父进程的操作 :发送信息给子进程
       char buf[50]="";
        while(1){
			//bzero 清空数组内存垃圾值
            bzero(buf,sizeof(buf));
            scanf("%s",buf);
            write(pipefd[1],buf,strlen(buf));
        }
       
    }else if(pid==0){
       //子进程的操作,读取无名管道中的内容
       char buf[50];
       while(1){
           bzero(buf,sizeof(buf));
           read(pipefd[0],buf,sizeof(buf));
           printf("子进程收到:%s\n",buf);
       }
    }else{
        perror("fork");
        exit(-1);
    }
       
    return 0;
}

3、管道读写注意

当管道中无数据时,读操作会阻塞
向管道中写入数据时, linux 将不保证写入的原子 性,管道缓冲区一有空闲区域,写进程就会试图
向管道写入数据。如果读进程不读走管道缓冲区 中的数据,那么写操作将会一直阻塞。
只有在管道的读端存在时,向管道中写入数据才 有意义。否则,向管道中写入数据的进程将收到
内核传来的 SIFPIPE 信号 ( 通常 Broken pipe 错误 )

二、有名管道

1、有名管道特点

有名管道(FIFO)是对无名管道的改进,具有以下特点:

(1)实现两个互不相关进程之间的彼此通信。

(2)该管道可以通过路径来指出,并且在系统中是可见的,建立后两个进程就可以将其当作普通文件进行读写操作。

(3)FIFO严格遵循先进先出规则,不支持如lseek() 操作(定位)。

模型:

2、测试:

通信方1:

#include <stdio.h>
#include <sys/types.h>//mkfifo头文件
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[]){
    if(argc!=2){
        printf("User:%s <file_name> \n",argv[0]);
        return 0;
    }
    //创建管道文件(利用某一个文件名只能创建一次的特性)
    if(access(argv[1],F_OK)){  //判断argv[1]是否已经存在
            if(mkfifo(argv[1],0666)){
                perror("mkfifo");
                exit(-1);
            }
    }
    
    
    //2>利用有名管道进行进程间通信 
    int fd=open(argv[1],O_RDWR);
    if(fd<0){
        perror("open");
        exit(-1);
    }
    //3>往有名管道里写入数据 
    char buf[50]="";
    while(1){
        bzero(buf,sizeof(buf));
        scanf("%s",buf);
        write(fd,buf,strlen(buf));
        }
    
    return 0;
}

通信方2:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[]){
    if(argc!=2){
        printf("User:%s <file_name> \n",argv[0]);
        return 0;
    }
    //创建管道文件(利用某一个文件名只能创建一次的特性)
    if(access(argv[1],F_OK)){  //判断argv[1]是否已经存在
            if(mkfifo(argv[1],0666)){
                perror("mkfifo");
                exit(-1);
            }
    }
    
    //2>利用有名管道进行进程间通信 
    int fd=open(argv[1],O_RDWR);
    if(fd<0){
        perror("open");
        exit(-1);
    }
    //3>往有名管道里读取数据 
       char buf[50];
       while(1){
           bzero(buf,sizeof(buf));
           read(fd,buf,sizeof(buf));
           printf("%s\n",buf);
       }
    
    return 0;
}

3、 读取操作:


三、信号通信

1、概述:

        信号是在软件层次上对中断机制的一种模拟,是 一种异步通信方式 。信号可以直接进行用户空间进程和内核进程之间 的交互,内核进程也可以利用它来通知用户空间 进程发生了哪些系统事件。
          如果该进程当前并未处于执行态,则该信号就由 内核保存起来,直到该进程恢复执行再传递给它; 如果一个信号被进程设置为阻塞,则该信号的传 递被延迟,直到其阻塞被取消时才被传递给进程。

2、生存周期:

 3、信号的响应:

(1)忽略信号:对信号不做任何处理,但是有两个信号不能 忽略:即SIGKILLSIGSTOP

(2)捕捉信号:定义信号处理函数,当信号发生时,执行相 应的处理函数。

(3)执行缺省操作:Linux对每种信号都规定了默认操作。

4、信号的检测于处理流程:

5、信号的种类:

 常用信号:

1) SIGHUP ;--->终端在关闭时,给其下所有的进程发送信号 
2) SIGINT :--->结束进程  就是:ctrl + c 
3) SIGQUIT: --->结束进程  就是:ctrl + \
9) SIGKILL: --->强制杀死进程  不能被注册,不能被忽略 --->signal 
10) SIGUSR1:--->用户自定义信号  --->signal
12) SIGUSR2:--->用户自定义信号  --->signal
14) SIGALRM:-->闹钟信号,用来倒计时,当到0后,触发信号,结束进程 
17) SIGCHLD:--->子进程结束时,给父进程发送的信号 
18) SIGCONT:--->继续进程 
19) SIGSTOP:--->暂停进程: 不能被注册,不能被忽略 --->signal
20) SIGTSTP:-->暂停进程: ctrl + z

6、发送信号:

                1>发送信号 
                   kill
                   #include <sys/types.h>
                   #include <signal.h>
                   int kill(pid_t pid, int sig);
                   功能: 
                        发送信号 
                        
                   参数: 
                           pid:接收信号的进程PID:发送给谁 
                           sig:发送的信号是什么
                           
                   返回值: 
                           成功返回0 
                           失败返回-1,并设置错误码
                           
                2>函数转换类型: 
                    atoi --->char型转int型
                    #include <stdlib.h>
                    int atoi(const char *nptr);
                    功能: 
                          将接收到的char型转化为int型 
                          
                    参数: 
                            nptr:接受到的char型 
                            
                    返回值: 
                            成功返回转化后int型
                            失败返回-1,并设置错误码
                            
                    从左往右进行转换的

7、信号处理:signal()函数:

                        信号注册:
                        signal
                        #include <signal.h>
                       typedef void (*sighandler_t)(int);
                       sighandler_t signal(int signum, sighandler_t handler);
                       功能: 
                            信号注册 
                            
                       参数: 
                             signum:需要操作的信号 
                             handler:注册事件 
                                   SIG_IGN:忽略 
                                   SIG_DFL:默认:恢复  
                                   捕捉:注册:执行自己写的一个函数
                                   typedef void (*sighandler_t)(int);--->函数指针
                                   
                       返回值: 
                            成功返回sighandler_t类型 
                            失败返回-1,并设置错误码\

8、定时器函数:

        pause函数,alarm函数:                  

#include <unistd.h>
            int pause(void);
            功能:
                    将进程挂起(休眠等待唤醒)--->阻塞 
                    唤醒:捕捉信号,执行该信号则解除挂起-->唤醒
               
            参数: 
                   void
                    
            返回值: 
                    成功返回0 
                    失败返回-1,并设置错误码
            
            
            alarm
            #include <unistd.h>
            unsigned int alarm(unsigned int seconds);
            功能: 
                    倒计时(计时器)
                    当倒计时到0时,触发SIGALRM信号,结束进程 
                    
            参数: 
                    seconds:秒数 
                    
            返回值: 
                    第一次调用返回0
                    二次调用以上,返回剩余秒数

9、测试:代码:

当父进程接受到cltr+c操作时,子进程则打印:我们要去佤邦发财。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
//收到信号后执行的函数,通过信号注册传递
void showMsg(int sig){
        printf("我们要去佤邦发财啦!\n");
}
//捕捉信号
void sendMsg(int sig){
    kill(getppid(),SIGUSR1);
}

int main(){
    //因为操作要开出父子进程:fork 
    pid_t pid=fork();
    if(pid>0){
        //父进程操作
        signal(SIGUSR1,showMsg);
        signal(SIGINT,SIG_IGN);
    }else if(pid==0){
        //子进程的操作
        signal(SIGINT,sendMsg);
    }
    
    while(1){
      //防止过快,显示,也可以用pause()函数  
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/apple_71040140/article/details/132510197