进程间通信

    相较于线程间通信,线程间通信由于是在同一进程内的,对于某些全局的静态变量进行读写操作,无疑方便的多。而不同进程之间的通信,由于在内存中位置不同,因此通信起来较为复杂,同时进程间通信有时还会考虑进程之间互斥访问的问题,及在多个进程同时就使用某一资源通信时存在的竞争。

    进程通信的方式,我目前了解到的有:管道(有名和匿名)、消息队列、共享内存,在此基础上会介绍操作系统提供的信号量(实现进程通信时互斥访问)

1、管道:管道分为有名管道和匿名管道。其中匿名管道是存在与内存中的,只能用于具有亲缘关系的进程之间进行通信,及通过fork创建出来的父子进程或兄弟进程。而有名管道则是通过使用路径名进行创建的(类似于文件),存在于文件系统之中,可以实现两个互不相关的进程之间的通信。管道传输的是无格式的字节流,且缓冲区的大小受到限制(使用ulimit -a可以查看);数据只能有一个进程流向另一个进程,如果要实现双端通信,则需要建立两个管道。

    下面是一个有名管道的例子:

//read.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>


#define P_FIFO "/home/sakura/Desktop/p_fifo"



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

    memset(buf, 0, sizeof(buf));        //initialize memory
    if(0 == access(P_FIFO, F_OK)){        //if pipe existed, delete it
        execlp("rm", "-f", P_FIFO, NULL);
        printf("access now!\n");
    }

    if(mkfifo(P_FIFO, 0777) < 0){
	printf("create pipe failed\n");
    }

    fd = open(P_FIFO, O_RDONLY|O_NONBLOCK);        //open the pipe
    while(1){        //read
        memset(buf, 0, sizeof(buf));
        if(read(fd, buf, 100) == 0){
            printf("No data!\n");
        }
        else{
            printf("get data: %s\n", buf);
        }
        sleep(1);
    }

    return 0;
}


//write.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>


#define P_FIFO "/home/sakura/Desktop/p_fifo"

int main(int argc, char ** argv)
{
    int fd;
    if(argc < 2)
        printf("No input");
    
    fd = open(P_FIFO, O_WRONLY|O_NONBLOCK);
    write(fd, argv[1], 100);
    close(fd);
    return 0;
}

介绍几个函数:

int pipe(int fildes[2]);            //创建匿名管道

int mkfifo(const char *path, mode_t mode);    //创建匿名管道

详细参加:点击打开链接


2、消息队列:消息队列用于同一台机器上的进程间通信,是一个在系统内核中保存消息的队列,在系统内核中以消息链表的形式出现。

相关函数:

int msgget(key_t key, int msgflg);        //创建消息队列

通过一个指定的key值创建一个消息队列,其余进程可以通过同样的key值获取这个消息队列的msqid

,用以下的函数进行通信;msgflg表示的是消息队列的访问权限


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

一般而言,使用消息队列会使用以下的结构体

struct msg_st{
    long int msgType;    //指定消息类型
    char msgText;    //消息内容
}
主要是第二个参数,这里传入的是一个空指针,因此传入结构时需要进行转换(void *)&msg_st,后两个参数msgtyp会根据指定值获取消息队列中的对应的消息,具体解释参见上面链接中的内容。


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

int msgctl(int msqid, int cmd, struct msqid_ds *buf);    //设置消息队列属性

参数cmd,提供了几个指定的命令:

IPC_STAT:
将与msqid关联的msqid_ds数据结构的每个成员的当前值放入由buf指向的结构中。这个结构的内容在<sys / msg.h>中定义。
IPC_SET:
将与msqid关联的msqid_ds数据结构的以下成员的值设置为由buf指向的结构中找到的对应值:
msg_perm.uid
msg_perm.gid
msg_perm.mode
msg_qbytes

另外,msg_ctime时间戳应设置为当前时间,如IPC常规说明中所述。
IPC_SET:只能由具有适当特权的进程执行,或者其有效用户ID等于与msqid关联的msqid_ds数据结构中的msg_perm.cuid或msg_perm.uid的值。只有具有适当权限的进程才能提高msg_qbytes的值。
IPC_RMID:
从系统中删除由msqid指定的消息队列标识符,并销毁与之关联的消息队列和msqid_ds数据结构。 IPC_RMD只能由具有适当权限的进程执行,或者只有与msqid关联的msqid_ds数据结构中的有效用户ID等于msg_perm.cuid或msg_perm.uid的值。 

主要是最后一个IPC_RMID用于删除



代码示例:

//receive
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>

struct msg_st{
    long msgType;
    char msgText[BUFSIZ];
};

int main()
{
    int msg_id = -1;
    struct msg_st msg;
    int flag = 1;
    long int msg_type = 0;

    //establish the message queue
    msg_id = msgget((key_t)1234, 0666 | IPC_CREAT);     //0666 the  file permission

    if(-1 == msg_id){
        printf("create message queue error!\n");
        return -1;
    }
    //get the message from the queue
    while(flag){
        if(msgrcv(msg_id, (void*)&msg, BUFSIZ, msg_type, 0) == -1){
            fprintf(stderr, "can't receive the message!\n");
            exit(EXIT_FAILURE);
        }

        printf("Receive message: %s", msg.msgText);
        if(0 == strncmp(msg.msgText, "end", 3)){
            flag = 0;
        }
    }
    //delete the message queue
    if(msgctl(msg_id, IPC_RMID, 0) == -1){
        fprintf(stderr, "msgctl(IPC_RMID) failed!\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

//send

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>

struct msg_st{
    long msgType;
    char msgText[BUFSIZ];
};

int main()
{
    int msg_id = -1;
    struct msg_st msg;
    int flag = 1;
    long int msg_type = 0;
    char buf[BUFSIZ];

    //establish the message queue
    msg_id = msgget((key_t)1234, 0666 | IPC_CREAT);     //0666 the  file permission

    if(-1 == msg_id){
        printf("create message queue error! maybe get the id error\n");
        return -1;
    }
    //get the message from the queue
    while(flag){
	printf("Enter msg:");
	fgets(buf, BUFSIZ, stdin);
	strcpy(msg.msgText, buf);
	msg.msgType = 1;
        if(msgsnd(msg_id, (void *)&msg, BUFSIZ, 0) == -1){
            fprintf(stderr, "can't sent the message!\n");
            exit(EXIT_FAILURE);
        }

        if(0 == strncmp(msg.msgText, "end", 3)){
            flag = 0;
        }
	sleep(1);
    }
    //delete the message queue

    exit(EXIT_SUCCESS);
}

3、共享内存:共享内存是不同的进程将同一段物理内存映射到自己的内存空间,也就是说多个进程在物理上共用了同一块内存,若其中有一个程序对共享内存进行了修改,那么其他进程通过访问即可获得修改后的值,获得传输的数据。需要注意的是,需要对共享内存的结构有一个统一的解释。

先上代码:原型是生产者和消费者

//consumer
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include"shm_com.h"

#define SHM_KEY 1234
#define SEM_KEY 8086

union semun{
    int val;
};

int main()
{
    int shmid, semid;
    srand((unsigned)getpid());
    shmid = shmget(SHM_KEY, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
    if(-1 == shmid){
        fprintf(stderr, "shmget failed!");
        exit(EXIT_FAILURE);
    }

    void *shared_memory  = (void *)0;
    shared_memory = shmat(shmid, (void *)0, 0);
    if(shared_memory == (void *)0 - 1){
        fprintf(stderr, "shmat failed!");
        exit(EXIT_FAILURE);
    }

    printf("Memory attached at: %X\n", (long)shared_memory);
    struct shared_use_st *shared_stuff;
    shared_stuff = (struct shared_use_st *)shared_memory;
    shared_stuff->written = 0;

    semid = semget(SEM_KEY, 2, IPC_CREAT | 0666);
    union semun semun_t;
    semun_t.val = 0;
    semctl(semid, 0, SETVAL, semun_t);
    semun_t.val = 1;
    semctl(semid, 1, SETVAL, semun_t);

    struct sembuf sembuf_t;
    int running = 1;
    while(running){
        sembuf_t.sem_num = 0;
        sembuf_t.sem_op = -1;
        sembuf_t.sem_flg = SEM_UNDO;
        semop(semid, &sembuf_t, 1);

        if(shared_stuff->written){
            printf("Read from shared memory: %s", shared_stuff->text);
            sleep(rand() % 4);
            shared_stuff->written = 0;
            if(0 == strncmp(shared_stuff->text, "end", 3)){
                running = 0;
            }
        }

        sembuf_t.sem_num = 1;
        sembuf_t.sem_op = 1;
        sembuf_t.sem_flg = SEM_UNDO;
        semop(semid, &sembuf_t, 1);
    }

    if(-1 == shmdt(shared_memory)){
        fprintf(stderr, "shmdt failed!\n");
        exit(EXIT_FAILURE);
    }

    if(-1 == shmctl(shmid, IPC_RMID, 0)){
        fprintf(stderr, "shmctl(IPC_RMID) failed!\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}


//producer
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include"shm_com.h"

#define SHM_KEY 1234
#define SEM_KEY 8086

union semun{
    int val;
};

int main()
{
    int shmid, semid;
    srand((unsigned)getpid());
    shmid = shmget(SHM_KEY, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
    if(-1 == shmid){
        fprintf(stderr, "shmget failed!");
        exit(EXIT_FAILURE);
    }

    void *shared_memory  = (void *)0;
    shared_memory = shmat(shmid, (void *)0, 0);
    if(shared_memory == (void *)0 - 1){
        fprintf(stderr, "shmat failed!");
        exit(EXIT_FAILURE);
    }

    printf("Memory attached at: %X\n", (long)shared_memory);
    struct shared_use_st *shared_stuff;
    shared_stuff = (struct shared_use_st *)shared_memory;


    semid = semget(SEM_KEY, 2, 0666);

    struct sembuf sembuf_t;
    int running = 1;
    char buffer[BUFSIZ];
    while(running){
	sembuf_t.sem_num = 1;
        sembuf_t.sem_op = -1;
        sembuf_t.sem_flg = SEM_UNDO;
        semop(semid, &sembuf_t, 1);
        while(1 == shared_stuff->written){
            sleep(1);
            printf("Waiting for consuming...\n");
        }

        printf("Enter the text:");
        fgets(buffer, BUFSIZ, stdin);
        strncpy(shared_stuff->text, buffer, TEXT_SZ);
        shared_stuff->written = 1;
        if(0 == strncmp(shared_stuff->text, "end", 3)){
            running = 0;
        }

	sembuf_t.sem_num = 0;
        sembuf_t.sem_op = 1;
        sembuf_t.sem_flg = SEM_UNDO;
        semop(semid, &sembuf_t, 1);

    }
	if(-1 == shmdt(shared_memory)){
	    fprintf(stderr, "shmdt failed!\n");
	    exit(EXIT_FAILURE);
	}

	exit(EXIT_SUCCESS);
}

//shared memory
#ifndef _SHMCOM_H_HEADER_
#define _SHMCOM_H_HEADER_
#define TEXT_SZ 2048

struct shared_use_st
{
    /* data */
    int written;
    char text[TEXT_SZ];
};


#endif
(1)共享内存的创建:
shmid = shmget (SHM_KEY, sizeof ( struct shared_use_st), 0666 | IPC_CREAT);//int shmget(key_t key, int size, int flag)

    SHM_KEY是key_t类型

    shmget函数通过一个SHM_KEY,创建一个共享内存,第二个参数是大小,第三个参数是标志位即如何使用这块空间。如果对应的共享内存已存在,即返回该块的shmid

shared_memory = shmat (shmid, ( void * ) 0 , 0 );//void *shmat(int shmid, void *addr, int flag)

    shmat函数通过shmid将共享内存纳入自己的进程空间,addr以及flag参数决定以什么样的方式来将进程纳入自己的空间,函数返回值即是共享内存的地址(得到地址后,通过适当的类型转换即可使用)

int shmdt(const void *shmaddr);

    shmdt函数用于将共享内存从进程中剥离,成功返回0,失败返回-1

int shmctl(int shmid, int cmd, struct shmid_ds *buf)

    shmctl函数完成对共享内存的控制,其中cmd是控制指令,IPC_STAT(获得共享内存状态,复制到buf中),IPC_SET(改变共享内存状态,将buf指向的值复制到shmid代表的共享内存中),IPC_RMID(删除这片共享内存)

通过这几个函数,可以实现不同进程之间对于共享内存的操作,对于共享内存的读写,按照普通方式操作即可(前提是将共享内存转换成正确的结构类型)


代码中的几处sem使用了SYSTEM V信号量进行对内存的互斥访问,有一篇很详细的博客讲解信号量,附上链接,侵删

点击打开链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值