linux进程间通信

进程间通信:一共6种 ,信号、信号量、管道(只能亲缘进程间通信)(匿名管道 pipe、命名管道mkfifo)、消息队列、共享内存(效率最高)、套接字(网络)。

信号:(一般不可靠) signal) 是一种处理异步事件的方式
1.信号的概念:
信号是软件层面的中断,信号的响应依赖于中断
并发:
同步
异步:异步时间的处理:查询法,通知法
信号量:( Semaphore) 进程间通信处理同步互斥的机制 。是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。 例如互斥锁  pthread_mutex_t
2.相关命令:kill -l 查看所有的信号
1-31的为标准信号
34及以后的为实时信号


3.signal函数
signal(信号no,注册行为的函数):给一个指定的信号注册一个行为
信号no:可以使用宏值,也可以使用宏,见kill -l列表
注册行为的函数:可以是SIG_IGN(忽略)
也可以是SIG_DEL(默认行为)
或者就是一个函数的入口地址
信号会打断阻塞的系统调用

void(*signal(int signum,void(*func)(int))(int);

 

4.信号的行为是不可靠的
第一个信号还未响应完毕,又来了另一个相同的信号,那么信号处理的行为将会不可靠

5.可重入的函数
在信号处理中,第一次调用未结束,发生第二次信号调用处理函数,那么程序不出任何的问题,
代表该函数是可重入的,所有的系统调用都是可重入的,一部分库函数是可重入的,
如:rand是不可重入的,但提供了一个rand_r




6.常用函数:
kill(pid,sig):给进程发送信号,成功返回0,失败返回-1,并设置errno


alarm(秒数):隔多少秒之后发送一个SIGALRM信号

pause():会令目前的进程进入睡眠状态,直到收到信号中断

练习:做一个程序,让其跑5秒钟,做变量累加,查看结果
 
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
 
 static long i=0;
 void fun_hander(int s)
 {
    printf("%ld\n",i);
     exit(1);
 }
int main()
{
   // long i=0;
    alarm(5);//用闹钟定时5s
    signal(SIGALRM,fun_hander);//利用signa函数重新定位到fun_hander函数当中
    while(1)
    {
     i++;
    }
//printf("%ld\n",i);
}
1.打开文件,可能打开失败,判断假错,如果是信号打断的,需要从新再执行打开

2.读文件写文件

流量控制



令牌桶




管道:
匿名管道:
pipe(管道文件描述符数组):创建一个管道文件,拥有读写两端
管道文件描述符数组:0下标是读端,1下标是写端

用管道实现进程间通信,父子进程都会拿到读写两端,使用什么端就关闭另一端
管道是一个单工的

 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFSIZE 1024
int main(void)
{
    int arr[2];
    pid_t pid;
    char buf[BUFSIZE];
    pipe(arr);//创建一个管道
    
    pid = fork();//创建一个进程
    if(pid <  0)
    {
        perror("fork()");
        exit(1);
    }
    if(pid == 0)
    {
        //child
        close(arr[1]);//关闭数据写端
        read(arr[0],buf,BUFSIZE);//读取管道数据
        
        puts(buf);//打印获得数据
        close(arr[0]);
        exit(0);
    }else//父进程给子进程发消息
    {
        //parent
        
        close(arr[0]);//关闭管道读端
        write(arr[1],"hello",6);//数据写入管道写端
        close(arr[1]);//写完关闭管道写端
        wait(NULL);//爸爸等着给儿子收尸
    }
    exit(0);
}

mpg123 -:从文件或标准输入中获取数据解码播放
cat *.mp3 | mpg123 -

 
共享内存ipcs:
ipcs:查看当前进程中所有ipc对象
ipcrm :删除一个ipc对象
-m:共享内存
-q:消息队列
共享内存对象都有一个id,是唯一标识符
有一个关联的key值,其余进程通过key获取到共享内存的id号


ftok(路径,proj_id):成功时返回一个合法的key值,失败时返回-1
路径:必须存在且可以访问的文件的路径
proj_id: 用于生成key的数字,会使用其低八位来与文件路径的inode运算,不能为0,一般给一个字符常量
简单实例:
 
    
key_t key;//声明一个key值
if((key = ftok(".",'a')) < 0)//计算出key值 file to key
    {
        perror("ftok()");
        exit(1);
    }


共享内存使用步骤:
1.创建/打开共享内存
shmget(key值,大小,shmflg):成功返回共享内存的id,失败时返回-1并设置errno
key值可以指定为:IPC_PRIVATE:就是0值,代表私有
            私有的只有拥有亲缘关系的进程才会访问得到,其他进程无法访问。
shmflg:共享内存标志位:IPC_CREAT|0666,私有的不用或上IPC_CREAT
多个进程使用共享内存通信,都会调用该函数,第一个调用的负责创建,
其余的负责根据key值找到共享内存的路径返回其id

2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
shmat(要映射的共享内存id,映射后的地址,标志位):成功时返回映射后的地址,失败返回(void *)-1
映射后的地址:可以自己指明一块地址(自己创建比较麻烦),NULL表示系统自动映射
标志位:为0表示可读可写,SHM_RDONLY表示只读

3.读写共享内存
            通过读写映射后的地址

4.撤销共享内存映射,不使用内存共享时应撤销映射
            shmdt(共享内存映射的地址):成功返回0,失败返回-1
5.删除共享对象
shmctl(共享内存的id,cmd,NULL):删除共享内存段
cmd:IPC_RMID,删除

每块共享内存大小有限制
ipcs -l可以查看
cat /proc/sys/kernel/shmmax :可以查看或修改共享内存的大小


简单实例
shm.h
 
#ifndef SHM_H__
#define SHM_H__
#define DATASIZE 1024
typedef struct 
{
    int  flag;//  0:read   1:write
    char data[DATASIZE];
}msg_t;
#endif
shma.c
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm.h"
#define SHMSIZE 1024
int main(void)
{
    key_t key;//声明一个key值
    
    int shmid;
    
    msg_t *msg;
    //msg.addr = NULL;
     //1.计算出一个key
    if((key = ftok(".",'a')) < 0)//计算出key值 file to key
    {
        perror("ftok()");
        exit(1);
    }
      //2.创建一段共享内存
    if((shmid = shmget(key,SHMSIZE,IPC_CREAT|0666)) < 0)//创建一端共享内存空间
    {
        perror("shmget()");
        exit(1);
    }
     //映射到该地址 msg_t *msg 
    if((msg = shmat(shmid,NULL,0)) == (void *)-1)//映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
    {
        perror("shmat()");
        exit(1);
    }
    
    msg->flag = 1;
    //write
    while(1)
    {
        if(msg->flag)
        {
            fgets(msg->data,SHMSIZE,stdin);//将数据写到标准输入流当中
            msg->flag = 0;
        }
    }
    if(shmdt(msg) < 0)//撤销共享内存映射,不使用内存共享时应撤销映射
    {
        perror("shmdt()");
        exit(1);
    }
    if(shmctl(shmid,IPC_RMID,NULL) < 0)删除共享对象
    {
        perror("shmctl()");
        exit(1);
    }
    
    exit(0);
}   
shmb.c
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm.h"
#define SHMSIZE 1024
int main(void)
{
    key_t key;
    
    int shmid;
    
    msg_t *msg;
    if((key = ftok(".",'a')) < 0)
    {
        perror("ftok()");
        exit(1);
    }
    if((shmid = shmget(key,SHMSIZE,IPC_CREAT|0666)) < 0)
    {
        perror("shmget()");
        exit(1);
    }
    if((msg = shmat(shmid,NULL,0)) == (void *)-1)
    {
        perror("shmat()");
        exit(1);
    }
    //read
    while(1)
    {
        if(!(msg->flag))
        {
            fputs(msg->data,stdout);
            msg->flag = 1;
        }
    }
    if(shmdt(msg) < 0)
    {
        perror("shmdt()");
        exit(1);
    }
    if(shmctl(shmid,IPC_RMID,NULL) < 0)
    {
        perror("shmctl()");
        exit(1);
    }
    
    exit(0);
}   

消息队列:
1.打开消息队列:
msgget(key,msgflg):成功返回消息队列id,失败时返回-1
key:和消息队列所关联的key值
msgflg:标志位,IPC_CREAT|0666

2.向消息队列发送消息:
msgsnd(消息队列id,发送消息的地址,消息的大小):成功返回0,失败返回-1
标志位:0或IPC_NOWAIT,0代表发送成功该函数才返回,另是不管成功与否

    消息格式:
双方约定消息的格式,采取结构体形式
首成员必须为 long,代表消息的类型,接受者根据类型接受指定的数据包
后面的成员都为数据成员,真正发送的消息应该要减去long成员

3.从消息队列接收消息:
msgrcv(消息队列的id,接收到的消息存放处,指定要接收的消息长度,指定接收的消息的类型(对应发送时的设置),标志位):
成功返回成功收到的消息长度,失败返回-1
标志位:0或IPC_NOWAIT,0代表发送成功该函数才返回,另是不管成功与否

        4.删除消息队列:
                msgctl(消息队列id,IPC_RMID,NULL):删除消息队列,成功返回0,失败返回-

简单实例:
msg.h
 
#ifndef _MSG_H
#define _MSG_H
#define DATASIZE 1024
typedef struct 
{
    long mtype;//首成员必须为long,代表消息的类型,接受者根据类型接受指定的数据包
    char data[DATASIZE];
}msg_t;
#endif
msga.c
 
#include"msg.h"
///
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include <sys/wait.h>
#include<signal.h>
#include<string.h>
#define BUFSIZE 1024
static int flag =1;
void kill_handle(int s)
{
  flag=0;
}
int main(void)
{
   key_t key;
   msg_t smsg,rmsg;
   pid_t pid;
   key=ftok(".",'a');
    if(key<0)
    {
        perror("ftok()");
        exit(1);
    }
//1.打开消息队列:
  int id= msgget(key,IPC_CREAT|0666);
    if(id<0)
    {
        perror("msgget()");
        exit(1);
    }
pid=fork();
  if(pid<0)
  {
    perror("fork()");
    exit(0);
  }
    if(pid==0)//子进程接收另一分文件发送过来的消息
    {
        signal(SIGKILL,kill_handle);
         while(flag)
         {
            // 3.从消息队列接收消息:
               msgrcv(id,&rmsg,BUFSIZE,200,0);
               printf("%s",rmsg.data);
         }
         exit(1);
    }
    else
    {
        while(1)
        {
            smsg.mtype=100;
            fgets(smsg.data,DATASIZE,stdin);
            //2.向消息队列发送消息:
            msgsnd(id,&smsg,DATASIZE,0);
            if(strncmp(smsg.data,"exit",4)==0)
            {
                //strcp();
                
                kill(pid,SIGKILL);
                break;
            }
           
        }
    }
   //4.删除消息队列:
    msgctl(id,IPC_RMID,NULL);
    exit(0);
}
msgb.c
 
#include"msg.h"
///
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include <sys/wait.h>
#include<string.h>
static int flag=1;
#define BUFSIZE 1024
void kill_handle(int s)
{
  flag=0;
}
int main(void)
{
   key_t key;
   msg_t rmsg,smsg;
  pid_t pid;
   char buf[BUFSIZE];
    key=ftok(".",'a');
    if(key<0)
    {
        perror("ftok()");
        exit(1);
    }
  int id= msgget(key,IPC_CREAT|0666);
    if(id<0)
    {
        perror("msgget()");
        exit(1);
    }
  pid=fork();
  if(pid<0)
  {
    perror("fork()");
    exit(0);
  }
  if(pid==0)
  { 
    signal(SIGKILL,kill_handle);
    while(flag)
    {
        smsg.mtype=200;
        fgets(smsg.data,DATASIZE,stdin);
        msgsnd(id,&smsg,DATASIZE,0);
    }
     exit(1);
  }else{
    while(1)
    {
        //msg.mtype=100;
        //fgets(msg.data,DATASIZE,stdin);
        msgrcv(id,&rmsg,BUFSIZE,100,0);
        if(strncmp(rmsg.data,"exit",4)==0)
        {
             kill(pid,SIGKILL);
            break;
        }
        printf("%s",rmsg.data);
    }
}
    msgctl(id,IPC_RMID,NULL);
   wait(NULL);
    exit(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值