Linux基础部分的学习(7)- 进程间通信


文章目录

  • 前言
  • 一、进程间的通信
    • 1.管道:
      • 1.无名管道
        • a.特点:
        • b.原型:
        • c.示例
        • d.代码实现:
      • 2.命名管道:
        • a.特点:
        • b.原型:
        • c.代码示例:
    • 2.消息队列:
      • 1.简介:
      • 2.原型:
      • 3.代码
    • 3.共享内存:
      • 1、特点:
      • 2.原型:
      • 3.代码实现:
    • 4.信号
      • 1.概述:
      • 2.信号携带消息编程
        • 原型:
    • 5.信号量
      • 1.简述:
      • 2.原型:
      • 3.代码实战:


前言

坚志者,功名之主也。不惰者,众善之师也。——《抱朴子》


一、进程间的通信

前面介绍的创建进程,且进程之间交换信息的方法是经过fork,exec传送打开文件,或者通过文件系统进行通信,有点繁杂和残缺。现在我们学习一种进程间相互通信的其他技术 –LINUX IPC继承了(System V IPC 和 基于 Socket IPC)

IPC的几种主要手段有:管道(无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等、其中后二者支持在不同主机上的两个进程IPC

1.管道:

1.无名管道

a.特点:

1.半双工,有固定的读、写端
2.只能在具有亲缘关系的进程间进行通信
3.管道中不储存数据,读取后消失
4.可以把它看成是一个特殊的文件,对他的读写可以使用read,write等函数,但它不是普通文件,并且不属于任何文件系统,只存在于内存之中。

b.原型:

#include 
int pipe(int fd[2]);		//返回值;若是成功返回0,失败返回1 
当一个管道建立的时候,会返回俩个描述符:  fd[0] 读打开
									fd[1] 写打开
关闭管道即关闭两个文件描述符

Linux基础部分的学习(7)- 进程间通信_第1张图片

c.示例

在父进程创建子进程,两个进程之间IPC通信

Linux基础部分的学习(7)- 进程间通信_第2张图片

d.代码实现:

#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
        int pid;
        int fd[2];
        char readBuf[200];

        if(pipe(fd) == -1)
                printf("creat pipe fail\r\n");

        pid = fork();
        if(pid < 0)
                printf("creat fork fail\r\n");

        else if (pid > 0){      //进入父进程
                sleep(3);       //让子进程先运行
                printf("This is father fork\r\n");
                close(fd[0]);
                write(fd[1],"Hello this is test",strlen("Hello this is test"));
                wait(NULL);
        }

        else{           //进入子进程
                printf("This is child fork\r\n");
                close(fd[1]);           //关闭写通道准备读取数据
                read(fd[0],readBuf,200);
                printf("The rec is %s\r\n",readBuf);
                exit(0);
        }


        return 0;
}
 

执行结果:
Linux基础部分的学习(7)- 进程间通信_第3张图片

2.命名管道:

a.特点:

除了无名管道的功能外,他还允许无亲缘关系进程间的通信

b.原型:

#include 

int mkfifo(const char *pathname,mode_t mode);		//成功返回0,失败返回-1
mode参数和open参数相同,创建一个fifo可以一般文件I/O函数操作它
当打开一个FIFO时候
若没有指定O_NONBLIOCK(默认),只读open会阻塞到某个进程为写而打开此FIFO,类似只写open会阻塞到某个进程为读而打开
若指定了O_NONBLOCK,则只读open立即返回,而只写open将出错返回-1,如果没有进程已经为读而打开该FIFO,其errno置ENXIO

c.代码示例:

write.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
        int fd;
        char writeBuf[]={"This is a test\r\n"};
        fd = open("./test",O_WRONLY);
        if(fd < 0 ){
                printf("Open test fail\r\n");
                exit(-1);
        }
        int nums =5;
        while(nums --)
        {
                write(fd,writeBuf,strlen(writeBuf));
                sleep(1);

        }
        close(fd);
        return 0;
}

read.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
        char readBuf[100]={0};
        if(mkfifo("./test",0600) < 0&& errno != EEXIST){
                printf("Open pipe fail\r\n");
                perror("Why: ");
        }
        int fd = open("test",O_RDONLY);
        if(fd < 0){
                printf("Opne fail\r\n");
                exit(-1);
        }
        while(1){
                int read_n = read(fd,readBuf,100);
                if(read_n <= 0)
                        exit(-1);
                printf("Read num is %d,the sec is %s\r\n",read_n,readBuf);
        }
        close(fd);
        return 0;
}

2.消息队列:

1.简介:

消息队列是消息的链接表,存放在内核中,一个队列由一个标识符(即队列ID)来标识
消息队列克服了信号承载信号量少、管道只能承载无格式字节流以及缓冲区大小受限的缺点

2.原型:

#include 
#include 
#include 

key_t ftok(char *pathname,char proj);		//获取键值
pathname 为文件名	proj为项目名

int	msgget(key_t key int msgflg);		//打开/创建一个消息队列
key为键值	msgflg为标志位
msgflg: IPC_CREAT :创建一个新的消息队列
		IPC_EXCL和IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,返回错误
		IPC_NOWAIT表示读写消息队列要求无法满足的时候,不阻塞

int	msgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg);		//向消息队列发送一条消息
msqid:为打开的消息队列ID;
msgp:为存放消息的结构;
msgflg:为发送标志,


int msgrcv(int msqid,struct msgbuf*msgp,int msgsz,long msgtyp,int msgflg);		//接收消息,成功返回0,失败返回-1
打开msqid的消息队列,把消息存放在msgp指向的msgbuf结构中,读取成功后这条消息会被删除


int msgctl(int msqid, int cmd, struct msqid_ds *buf);		//消息控制

3.代码

msgwrite.c


#include 
#include 
#include 
#include 
#include 
#include 

struct msgbuf{
        long mtype;
        char mtext[128];
};


int main()

{
        struct msgbuf readbuf;
        struct msgbuf sendbuf = {
        888,
        "This is a test from msgwrite"
        };

        key_t key;
        key = ftok(".",'z');
        if(key < 0){
                printf("ftok error \r\n" );
                exit(-1);
        }
        printf("The key num is %d\r\n",key);
        int msgid = msgget(key,IPC_CREAT|0777);
        if(msgid < 0){
                printf("get error\r\n");
                exit(-1);
        }
        msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),988,0);
        printf("The rec is %s\n",readbuf.mtext);
        return 0;

}

msgread.c

#include 
#include 
#include 
#include 

struct msgbuf{
         long mtype;
         char mtext[256];
};

int main()

{

         int msqid;
         key_t key;
         struct msgbuf readbuf;
        struct msgbuf sendbuf = {
                988,
                "Have read your send"
        };

         key = ftok(".",'z');
         if(key < 0){
                 printf("ftok error\r\n");
                 exit(-1);
         }
         printf("Message Queue - Server key is: %d.\n", key);

         if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
                      {
                                  perror("msgget error");
                                  exit(1);
                      }
        msgrcv(msqid,&readbuf,sizeof(readbuf.mtext),888,0);
        msgsnd(msqid,&sendbuf,strlen(sendbuf.mtext),0);
        printf("The sec is %s\r\n",readbuf.mtext);
        return 0;
}

结果分析
在这里插入图片描述

3.共享内存:

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区

1、特点:

共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

2.原型:

#include 

int shmget(key_t key,size_t size,int flag);		//共享内存的创建,成功时返回内存的ID,失败返回-1

int	shmctl(int shm_id,int cmd,struct shmid_ds*buf);		//对内存的操作

void *shmat(int shm_id,const void*addr,int flag);		//共享内存段连接到本进程空间	成功返回指向内存的指针

int	shmdt(void *addr);							//解除内存

3.代码实现:

shmwrite.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{

        key_t key;
        key = ftok(".",'a');
        if(key < 0){
                printf("Ftok creat error\r\n");
                exit(-1);
        }
        printf("key num is %d\n",key);

        int shm_id = shmget(key,1024*4,IPC_CREAT|0666);
        if(shm_id < 0){
                printf("creat shmid fail\r\n");
                exit(-1);
        }
        printf("shm_id is %d\r\n",shm_id);

        char *shmaddr = shmat(shm_id,0,0);
        if(shmaddr < 0){
                printf("shmat creat fial\r\n");
                exit(-1);
        }
        printf("shmat success!!!\r\n");
        strcpy(shmaddr ,"This is a project");

        sleep(10);
        shmdt(shmaddr);
        shmctl(shm_id,IPC_RMID,0);
        printf("quit success !!!\r\n");

        return 0;
}

shmread.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{

        key_t key;
        key = ftok(".",'a');
        if(key < 0){
                printf("Ftok creat error\r\n");
                exit(-1);
        }
        printf("key num is %d\n",key);

        int shm_id = shmget(key,1024*4,0);
        if(shm_id < 0){
                printf("creat shmid fail\r\n");
                exit(-1);
        }
        printf("shm_id is %d\r\n",shm_id);

        char *shmaddr = shmat(shm_id,0,0);
        if(shmaddr < 0){
                printf("shmat creat fial\r\n");
                exit(-1);
        }
        printf("shmat success!!!\r\n");
        printf("The net is %s\r\n",shmaddr);

        shmdt(shmaddr);
        printf("quit success !!!\r\n");

        return 0;
}

将shmwrite.c中释放共享内存注释,利用	 ipcs	-m		指令观察当前的共享内存段

Linux基础部分的学习(7)- 进程间通信_第4张图片

利用		ipcrm -m 共享内存的id号码  可以关闭共享内存

Linux基础部分的学习(7)- 进程间通信_第5张图片

4.信号

1.概述:

信号是进程在运行的过程中,有自身产生或进程外部发过来的,
用来通知进程发生了异步事件的通信机制,是硬件中断的软件模拟(软中断),是进程间通信机制中唯一的异步通信机制。

kill -l显示所有信号

信号处理方法: 忽略、捕捉、和默认动作


将传入的ctrl+c信号进行修改,让其执行设定好的函数

Linux基础部分的学习(7)- 进程间通信_第6张图片
执行结果:

Linux基础部分的学习(7)- 进程间通信_第7张图片

2.信号携带消息编程

原型:

int sigaction(int signum,const struct sigaction*act,struct sigaction *oldact)
signum 是信号
act是struct sgaction指针
					包括:1,sa_sigaction:指定要处理的函数
						 2,sa_flags:指定处理信号的方式
						 3,sa_mask:阻塞信号集

sigproread.c

#include 
#include 
#include 


void handler (int signum ,siginfo_t *info,void *ucontext)
{
        if(ucontext == NULL){
                printf("NOT recevie sig\r\n");
                exit(-1);
        }
        printf("Have rec is %d\n",signum);
        printf("Send pid is %d\n",info->si_pid);
        printf("The value is %d\n",info->si_value.sival_int);
        printf("The data is %d\n",info->si_int);
}


int main()
{

        struct sigaction act;
        printf("Now forkpid is %d\r\n",getpid());
        act.sa_sigaction = handler;
        act.sa_flags = SA_SIGINFO;//可以发送数据 

        sigaction(SIGUSR1,&act,NULL);
        while(1);
        return 0;
}

sigprowrite.c

#include 
#include 
#include 
#include 
#include 


int main(int argc ,char **argv)
{
        int signum = atoi(argv[1]);
        int pid = atoi(argv[2]);
        printf("argv[1] = %s\n",argv[1]);

        union sigval value;
        value.sival_int = 666;

        printf("The signum is %d\n",signum);
        printf("Now forkpid is %d\n",getpid());

        sigqueue(pid,signum,value);
        printf("send success\r\n");

        return 0;
}

5.信号量

1.简述:

信号量是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据

用于进程间的永不,若在进程间要传递数据要结合共享内存实现
信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作
支持信号量组

2.原型:

#include 

int semget(key_t key,int num_sems,int sem_flags);//创建/获取一个信号量组,成功返回信号量集的ID,失败返回-1

int semop(int semid,struct sembuf semoparray[].size_t numops);//对信号量组进行操作,改变信号量的值;成功返回0,失败返回-1
			
int	semctl(int semid ,int sem_num,itn cmd, ...); // 控制信号量的相关信息

3.代码实战:

先让子进程释放信号量,再让父进程运行

#include 
#include 
#include 
#include 
#include 
#include 

union semun{

        int val;
        struct semid_ds *buf;
        unsigned short *array;
        struct seminfo *_buf;

};

void Getkey(int id)
{
        struct sembuf so;
        so.sem_num =0;
        so.sem_op = -1;
        so.sem_flg = SEM_UNDO;
        if(semop(id,&so,1) < 0){
                printf("Get error\r\n");
                exit(-1);
        }
        printf("Get succes\r\n");

}

void Putkey(int id)
{
        struct sembuf so;
        so.sem_num =0;
        so.sem_op = 1;
        so.sem_flg = SEM_UNDO;
        if(semop(id,&so,1) < 0){
                printf("Put  error\r\n");
                exit(-1);
        }
        printf("Put succes\r\n");

}

int main(int argc,char **argv)
{
        int semid;
        key_t key ;
        key = ftok(".",1);
        if(key < 0){
                printf("ftok create fail \r\n");
                exit(-1);
        }

        semid = semget(key,1,IPC_CREAT|0666);
        if(semid < 0)
        {
                printf("smeid creat error\r\n");
                exit(-1);
        }
        union semun initsem;
        initsem.val = 0;

        semctl(semid,0,SETVAL,initsem);

        int pid;
        pid = fork();

        if(pid > 0){
                Getkey(semid);
                printf("This is father fork\r\n");
                Putkey(semid);
        }
        else if(pid == 0){
                printf("This is child fork\r\n");
                Putkey(semid);
        }
        else
        {
                printf("fork creat error\r\n");
                exit(-1);
        }

        return 0;

}

Linux基础部分的学习(7)- 进程间通信_第8张图片


你可能感兴趣的:(LINUX,linux,学习)