Linux进程间通信(IPC)的五种方式

进程间通信(IPC)的概念:进程间通信是指在不同进程间进行信息传输或交换,IPC的方式通常有管道,消息队列,信号量,共享存储,Socket,Stream等

  1. 无名管道,UNIX系统最古老的形式

特点:①.半双工,具有固定的读写

②:只能用于父子进程兄弟进程

③:.可以看做是一种普通文件,他的读写可以用read和write等函数,但不是普通文件,他只存在于内存当中

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(){
        int fd[2];
        char buf[128];
        int pid;
        char *writeData = "this is write_data";
        if(pipe(fd) == -1){
                printf("pipe create failed!\n");
        }

        pid = fork();

        if(pid == 0){
                sleep(1);
                close(fd[1]);
                read(fd[0],buf,128);
                printf("read data: %s\n",buf);
                exit(0);
        }
        close(fd[0]);
        write(fd[1],writeData,strlen(writeData));
        wait(NULL);
        return 0;
}
  1. 有名管道

函数有:

mkfifo(path,mod); 创建管道

path路径名,mod模式

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>

int main(){
        if(mkfifo("./file",0600)==-1 && errno == EEXIST){
                printf("mkfifo failed!\n");
                perror("why");
                exit(0);
        }
        printf("mkfifo success!\n");
        return 0;
}

这是以创建一个文件来进行管道传输的,使用这个有名管道进行通信时,要么读,要么写,不能同时

读的代码:

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

int main(){
        char buf[30]={0};
        int n_read;
        int fd = open("./file",O_RDONLY);
        printf("open success\n");
        n_read = read(fd,buf,30);
        while(n_read > 0){
                sleep(1);
                n_read = read(fd,buf,30);
                printf(buf);
        }
        close(fd);
        return 0;
}

写的代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
int main(){
        int i;
        char *str = "this is write data\n";
        int fd = open("./file",O_WRONLY);
        printf("open success\n");
        for(i = 0; i<5;i++){
                write(fd,str,strlen(str));
        }
        close(fd);
        return 0;
}

3.消息队列,是消息的链接表,存放在内核中,一个消息队列由一个标识符即队列ID来标识。

特点:1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级

2.消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除

3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

创建队列msgget

接收消息 msgrcv

发送消息 msgsnd

操作队列msgctl

收发消息的函数原型

 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

代码demo6

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

/*
 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);*
 */

struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main(){
        key_t key = ftok(".",1);
        int msgId;
        struct msgbuf msgread;
        //创建队列
        if(msgId = msgget(key,IPC_CREAT|0700) == -1){
                printf("create queue failed\n");
        }
        //接收消息
        msgrcv(msgId,&msgread,sizeof(msgread.mtext),888,0);
        printf("receive:%s\n",msgread.mtext);

        //再发消息
        struct msgbuf msgsend = {999,"thanks!"};
        msgsnd(msgId,&msgsend,strlen(msgsend.mtext),0);
        //删除队列
        msgctl(msgId,IPC_RMID,NULL);
        return 0;
}

代码demo7

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

/*
 nt msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);*
 */

struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};


int main(){
        key_t key = ftok(".",1);
        int msgId;
        struct msgbuf msgwrite = {888,"aaa"};
        //创建队列
        if(msgId = msgget(key,IPC_CREAT|0700) == -1){
                printf("create queue failed\n");
        }
        //发送消息
        msgsnd(msgId,&msgwrite,strlen(msgwrite.mtext),0);
        //再接收消息
        struct msgbuf msgrece;
        msgrcv(msgId,&msgrece,sizeof(msgrece.mtext),999,0);
        printf("rece:%s\n",msgrece.mtext);
        //删除队列
        msgctl(msgId,IPC_RMID,NULL);
        return 0;
}

4.共享内存

//创建或获取一个共享内存,成功则返回ID,失败返回-1

//映射共享内存(连接当前进程的地址空间和共享内存,成功返回指向共享内存的指针,失败返回-1)

//写入东西,就相当于直接copy内容到当前进程的映射地址即可

//断开连接共享内存

//删除共享内存

下面这两个是命令行指令

ipcs -m 在控制台能够查看现在共享内存的使用情况,

ipcrm -m XX 删除shmid == XX的这个共享内存

5.信号

5.1信号低级版

有分低级版和高级版

低级版就是没有进行消息的传输

代码如下

demo10,对命令行命令kill进行替换 ,可以使用kill -l 查看详细的对应字段

#include <stdio.h>
#include <signal.h>

void handler(int signum){
        printf("input sinnum is : %d",signum);
        switch(signum){
                case 2:
                printf("SIGINT\n");
                break;
                case 9:
                printf("SIGKILL\n");
                break;
                case 10:
                printf("SIGUSR1\n");
                break;
        }

        printf("can't kill\n");

}


int main(){
        signal(SIGINT,handler);
        signal(SIGKILL,handler);
        signal(SIGUSR1,handler);
        while(1);
        return 0;
}

demo11 ,下面的代码功能是,运行程序时传入signum和pid参数,对应的是你要在命令行执行的

kill -XX XX的指令 例如 kill -2 进程ID,原本执行的功能是中断,但替换后不会执行原有功能,而是执行我们自己编写的handler的功能

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>

int main(int argc,char **argv){
        int signum;
        int pid;
        char cmd[128] = {0};

        //这里要把数组里的字符串转换为int,用atoi   char to int
        signum = atoi(argv[1]);
        pid = atoi(argv[2]);

        sprintf(cmd,"kill -%d %d",signum,pid);
        printf(cmd);
        system(cmd);
        return 0;
}

5.2信号高级版

就是加入的消息的传输,个人认为有点操蛋。直接上代码吧。

函数原型

//函数原型
//int sigaction(int signum, const struct sigaction *act,
//                     struct sigaction *oldact);

/**
 *
 *struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };
*/

具体使用代码

接收端:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>


void handler(int signum,siginfo_t *info,void *context){
        printf("get signum : %d\n",signum);
        if(context != NULL){
                printf("siginfo1 : %d\n",info->si_int);
                printf("siginfo2 : %d\n",info->si_value.sival_int);
        }
}

int main(){
        struct sigaction act;
        act.sa_sigaction = handler;
        act.sa_flags = SA_SIGINFO;

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

发送端:

#include <stdio.h>
#include <signal.h>

int main(int argc,char **argv){
        int signum;
        int pid;
        signum = atoi(argv[1]);
        pid = atoi(argv[2]);

        union sigval value;
        value.sival_int = 100;

        sigqueue(pid,signum,value);
        printf("over\n");
        return 0;
}

6.信号量集

①信号量用于进程间同步,若要在进程间传递胡数据需要结合共享内存。

②信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。(P操作:拿锁。V操作:放回锁)

③每次对信号量的 PV 操作不仅限于对信号量值加 1 或 减1 ,而且可以加加减任意正整数。

④支持信号量组。

//创建或获取一个信号量组,成功会返回信号量集 ID ,失败返回 -1
int semget(key_t key, int nsems, int semflg);
//对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
int semop(int semid, struct sembuf *sops, unsigned nsops);    
//控制信号量的相关信息 (用于给信号量初始化)
int semctl(int semid, int semnum, int cmd, ...);

示例代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
//int semget(key_t key, int nsems, int semflg);




union semun {
         int              val;    /* Value for SETVAL */
         struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
         unsigned short  *array;  /* Array for GETALL, SETALL */
         struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};

//
对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
//int semop(int semid, struct sembuf *sops, unsigned nsops);    

//拿锁
void getKey(int semid){
        struct sembuf mysem1;
        mysem1.sem_num = 0;
        mysem1.sem_op = -1;
        mysem1.sem_flg = SEM_UNDO;
        semop(semid,&mysem1,1);
        printf("getkey\n");
}
//还锁
void putKey(int semid){
        struct sembuf mysem1;
        mysem1.sem_num = 0;
        mysem1.sem_op = 1;
        mysem1.sem_flg = SEM_UNDO;
        semop(semid,&mysem1,1);
        printf("putKey\n");
}
int main(){
        //创建一个信号量集
        key_t key;

        int semid = semget(key,0,IPC_CREAT|0700);

        //int semctl(int semid, int semnum, int cmd, ...);
        //初始化信号量集
        union semun initsem;
        initsem.val = 0;
        semctl(semid,0,SETVAL,initsem);

        //用父子进程来操作这个信号量
        int pid = fork();
        if(pid == 0){
                printf("this is son\n");
                //拿锁
                getKey(semid);
                exit(0);
        }
        printf("this is father\n");
        //还锁
        putKey(semid);
        wait();
        //拿锁
        getKey(semid);
        //还锁
        putKey(semid);
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值