进程间通信总结

进程间的信息交换

同一主机上进程通信
Unix进程间通信方式:PIPE(无名管道),FIFO(有名管道),Signal(x信号)
System V进程间通信方式:消息队列,信号量,共享内存
不同主机进程间通信
RPC:远程过程调用
Socket:网络通信方式

在同一宿主机的通信总结

先总结,有个大体映像
这里写图片描述
这里写图片描述

无名管道(PIPE)使用

  • PIPE通信是单向的
  • PIPE管道只能在具有亲缘关系进程实现通信。

看图学函数
这里写图片描述

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
int main(){

    int fd[2];
    pipe(fd);
   if(fork()==0){
        char buff[20];
        sleep(2);
        memset(buff,'\0',sizeof(buff));
        close(fd[1]);
        read(fd[0],buff,sizeof(buff));
        printf("%s",buff);
    }{
        close(fd[0]);
        write(fd[1],"fuck",5);
        wait(NULL);
    }
}
  • 每次写入数据应小于PIPE_BUF字节(默认4096) ,若缓冲区满会发生阻塞。
  • 要记得进程要关闭不使用的fd,如读进程要关闭fd[1]

Posix2 提供连个使用管道机制实现简单进程间通信的函数

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
  • popen会fork一个子进程去执行command
  • 要从子进程读数据type设为”r”, 反之,设为“w”
  • 返回的是一个标准IO库里的文件指针,我们可以从它读入和写入数据
  • pclose 见名知意
#include<stdio.h>
#include<stdlib.h>
int main(){

    FILE * f = popen("ls","r");
    char buff[256];

    while(fgets(buff,256,f))
         printf("%s",buff);
    pclose(f);
}

有名管道(FIFO)的使用

  • 解决了无名管道只能在有亲缘关系的进程间通信问题
  • 解决了无名管道在进程结束后会消失的问题,但不保存管道内文件。
  • 其实管道就是一种特殊的文件,用来进程间通信用的。
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>

int main(){
    //  if( fd=mkfifo("./fifo",0666) <0){
    //    perror("make fifio error");
    //}
    int fd = open("fifo",O_WRONLY);
    if(fd<0){
        perror("open error");
    }

    int ret=write(fd,"hello fifio",strlen("hello fifio"));
    if(ret<0){
        perror("write error");
    }
}

因为管道是一种特殊的文件,read,write方法同样适用。
cat < fifo同样可以查看管道内容。
使用管道的时候一定要有两个进程同时打开读端和写端

利用信号通信

产生信号
  • 进程给另一个进程发送信号
  • 内核创建一个信号
信号状态
  • 信号被发送,但没有引起任何动作,称为等待
  • 信号被正确发送到一个进程成为信号的递送
  • 信号的递送导致一段处理程序被执行被称为信号的捕捉
处理一个信号
  • 忽略
  • 捕捉
  • 执行默认操作
信号的基本操作
  • 产生信号
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);//给指定进程传送kill函数,这不能见名知意了...

#include <signal.h>
int raise(int sig);           //给当前进程发送信号

 #include <unistd.h>
unsigned int alarm(unsigned int seconds);//唤醒一个进程和设置定时
  • 自定义处理信号
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);

使用共享内存

#define SHM_SIZE 10000
#define SHM_MODE 0677
int main()
{
    int pid, status;
    int shmid;
    char *ptr,*shmptr;
    char buff[1000];
    char line[100];
    key_t key;
    struct shmid_ds state;

    if((key = ftok("/",123))<0){
        perror("ftok");
    }

    memset(buff,'\0',sizeof(buff));

    //1、先获得一个共享内存的id,多个进程要使用同一块内存必须使用相同id
        //key不是必须被ftok生成,但是必须要有所区别
    if( ( shmid=shmget(key,SHM_SIZE,SHM_MODE|IPC_CREAT))<0)
            perror("shmget error");
       // 2、获得id对应的共享内存地址
    if ( ( shmptr = shmat(shmid,0 ,0)) == (void *)-1)
            perror("shmat error");  
    //3、接下来就是不同进程对同一块内存进行操作
    pid = fork();
    if(pid<0){
        perror("fork error");
    }

    if(pid==0){
        printf("input the string and press a more (enter) to exit\n:");

        while(fgets(line,100,stdin)){

            if(strlen(line)<2){
                break;
            }
            strncat(buff,line,strlen(line));
        }
        memcpy(shmptr,buff,1000);

        if(shmdt(shmptr)<0)
            perror("shmctl");
    }else{
        wait(NULL);

        memcpy(buff,shmptr,1000);
        fputs(buff,stdout);

        shmctl(shmid, IPC_STAT ,&state);
        printf("%d shares the memory size %u\n",getpid(),state.shm_segsz);

        if(shmdt(shmptr)<0)
            perror("shmctl");

    }

}

这段代码的意思是fork一个子进程,父进程通过共享内存获得子进程输入的字符串。

int shmget(key_t key, size_t size, int shmflg);
key:指定一个标识,用来获得同一块共享内存。 shmflag是标识共享内存权限,创建要使用IPC_CREAT参数。
void *shmat(int shmid, const void *shmaddr, int shmflg);
用来获得共享内存的地址,与当前进程的地址空间建立练习。shmaddr指定共享内存映射地址,最好设为0int shmdt(const void *shmaddr);
用来分离共享内存和当前进程

使用XSI 信号量

#define SHM_SIZE 10000
#define SHM_MODE 0600
#define WRITE 1
#define READ 0

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int init_sem(int id, int num ,int val)
{
    union  semun u;
    u.val = val;
    //num是第几个信号量
    if( semctl( id, num ,SETVAL, u) == -1){
        perror("inital semaphore");
        return 0;
    }

    return 1;
}

int del_sem(int id, int v)
{
    union semun u;
    if( semctl(id, 0, IPC_RMID, u) ==-1){
        perror("delete semaphore");
        return -1;
    }
    return 0;
}

int do_p(int id,int num)
{
    struct sembuf b;
    b.sem_num=num, b.sem_op = -1, b.sem_flg = SEM_UNDO;
    if (semop( id , &b, 1)==-1){
        perror("p error");
        return 0;
    }
    return -1;
}


int do_v(int id ,int num)
{
    struct sembuf b;
    b.sem_num = num, b.sem_op = 1, b.sem_flg = SEM_UNDO;

    if (semop( id , &b, 1)==-1){
        perror("v error");
        return 0;
    }
    return -1;
}

int main()
{
    pid_t pid;
    key_t key;
    int shmid;
    char *shmptr;    
    int  id;
    int food=0;
    int semid;
    if((key = ftok("/",123))<0){
        perror("ftok");
    }
    //创建共享内存
    if( ( shmid=shmget(key,SHM_SIZE,SHM_MODE|IPC_CREAT))<0)
            perror("shmget error");  
    if ( ( shmptr = shmat(shmid,0 ,0)) == (void *)-1)
            perror("shmat error"); 

   //1、创建两个信号量,第二个参数是创建信号量集的个数
    id = semget(IPC_PRIVATE, 2, 0666|IPC_CREAT);

    if(id<0){
        perror("semget error");
    }
    //初始 可写不可读
   //Posix提供的是对信号量集的操作,我们再封装一下,可以对特定信号操作
    init_sem(id,READ,0);
    init_sem(id,WRITE,1);

    pid = fork();
    if (pid ==0){
        while(1)
        {
            do_p(id,READ);            
            food = *(int*)(shmptr);
            printf("消费了食物%d\n",food);
            do_v(id,WRITE);
            sleep(2);
        }

    }else{
        while(1)
        {   
            food = rand();            
            do_p(id,WRITE);            
            *(int*)(shmptr)=food;
            printf("生产了食物%d\n",food);
            do_v(id,READ);
            sleep(1);
        }
    }

}

XSI信号量比较难用的原因是因为 它只提供了对一组信号量集合的操作。可以跟Demo一样做一次封装比较容易使用,使得可以操作单个信号量。
值得庆幸的是,信号量和共享内存,消息队列一样提供了一套相似的api。

int semget(key_t key, int nsems, int semflg);

获得一个指向有nsems个信号的集合的标识符
在Linux平台上设置信号量的时候,要自己定义一个Union。

union semun
{
    int val;            //信号量的值 SETVAL设置
    struct semid_ds *buf;
    unsigned short *array;
};
 int semctl(int semid, int semnum, int cmd, ...);

通过semctl来设置信号量值,semnum来标识信号量集合中的第几个信号量。cmd来表示操作。使用方法可以看demo。
这里写图片描述

通过semop来进行pv操作,通过sembuf结构体来传值。
这里写图片描述

sem_op <0 消耗 信号量 ,sem_op>0 增加信号量。
sem_num域来标识集合中第几个信号量。

使用POSIX信号量

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<string.h>
char buff[1024];
sem_t sem;

void *func(void *a){
    sem_wait(&sem);
    while (strncmp("end", buff, 3) != 0)  
    {  
        printf("length:%d\n", strlen(buff)-1);  
        sem_wait(&sem);  
    }  

    pthread_exit(NULL);
}

int main(){
    int status;
    pthread_t pid;
    void* ret;
    status = pthread_create(&pid,NULL,func,NULL);
    sem_init(&sem,0,0);
    if(status<0){
        perror("create thread");
    }

    while( fgets(buff,1024,stdin)){

        sem_post(&sem);
        int length =strlen(buff);
        if('e'==buff[length-4]&&'n'==buff[length-3]&&'d'==buff[length-2])
            break;
    }
    pthread_join(pid,NULL);


}

Posix信号量是解决是XSI信号量的一些问题。简化了接口,没有信号量集。操作是原子的。(想一下,XSI的PV操作是怎么完成的。)
信号量分两种,有名和无名。
有名在多进程中使用,无名在单进程多线程下使用。

打开一个有名信号量

sem_t *sem_open(const char *name, int oflag);

使用无名信号量

int sem_init(sem_t *sem, int pshared, unsigned int value)

value是初始值,pshared非0表示在多个进程下使用信号量。

关闭一个有名信号量

int sem_close(sem_t *sem);

销毁一个有名信号量

int sem_unlink(const char *name);

销毁一个无名信号量

int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);//阻塞等待

int sem_trywait(sem_t *sem);//非阻塞,如果不能获得信号量,返回-1并设置errno为EAGAIN

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int  sem_post(sem_t *sem)//用来提升信号量

使用消息队列


struct message{
    long int  message_type;   
    char buff[100];
};//设置消息的格式, 第一个参数一定要是报文的类型,接受者可以根据类型选择是否接收。
int main(){

    int msgid;
    struct message data;
    if ((msgid =msgget(876378,0666|IPC_CREAT))<0){
        perror("msget error");
        exit(0);
    }

    while(1){
        printf("input string:");
        fgets(data.buff,100,stdin);


        if(msgsnd(msgid,&data,100,0)<0){
            perror("msgsnd");
            exit(0);
        }

        if ( strncmp(data.buff,"end",3)==0 ){
            printf("end");
            break;
        }

    }
}
struct message{
    long int  message_type;   
    char buff[100];
};
int main(){

    int msgid;
    struct message data;
    if ((msgid =msgget(876378,0666|IPC_CREAT))<0){
        perror("msget error");
        exit(0);
    }

    while(1){

        if(msgrcv(msgid,&data,100,0,0)<1){
            perror("msgsnd");
            exit(0);
        }
        if ( strncmp(data.buff,"end",3)==0 ){
            printf("end");
            break;
        }
        printf("receive from sender:%s\n",data.buff);
    }
}

消息队列实在无需多言,看demo就行。具体就man手册。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值