Linux1

linux process commuinte pipe signal sem shm

This project is maintained by wangfakang

有关进程间通信的讲解

IPC技术:进程通信,有以下几种种:

管道:1 有名管道 2 无名管道 3 System V IPC (信号量) 4 套接字 5 消息队列

一.有关管道的讲解

首先管道是用于两个不同进程之间的数据传递,即实现进程之间的通信,其思想就是共享某个存储区.

问题一有关管道的详解:

管道是半双工通信

  int  pipe(int fd[2]);
  int  main()
{
 int fd[2]={0};
 if(pipe(fd)==-1)
{
return -1;
}

char buffer[256]={0};

write(fd[1],"hello",5);

sleep(1);

read(fd[0],buffer,255);

printf("buffer=");

return 0;
}

结果为: hello

测试管道的大小:

原理是:当管道里面写满的时候就会阻塞

  int  main()
{
 int fd[2]={0};

 if(pipe(fd)==-1)
{
str
sleep

return -1;
}

while(1)
{
write(fd[1],'1',1);

printf("i=%d\n",i++);


}

}

结果是输出 i=65535

即是: 变化了16位,2的16次方,即64k, 每一页有4k 。

其大小可以在include/linux/limits/中的BUF_SIZE 就是管道的大小

两个进程的通信例子:

在子进程中写入hello在父进程中读出来

 int  main()
{
 int fd[2]={0};
 if(pipe(fd)==-1)
{
return -1;
}
pid_t pid=fork();
assert(pid!=-1);
if(0==pid)
{
sleep(1);
write(fd[1],"hello",5);
close(fd[0]);
close(fd[1]);
}

else
{
char buffer[256]={0};
read(fd[0],buffer,255);
printf("buffer=%s",buffer);
close(fd[0]);
close(fd[1]);
}

return 0;
}

问题一注意:
两个进程之间通信的时候,对于父进程到子进程的管道,父进程应该关闭读端(fd[0],反之则相反)   

int main()
{
    int fd[2];
    pipe(fd);
    pid_t pid =fork();
    assert(pid!=-1);
    if(pid==0)
{
   char buffer[256]={0};
char *p=buffer;
close(fd[1]);
 while(read(fd[0],buffer,255)>0)
{
   while(*p!='\0')
{
if(*p>='a'  &&  *p<='z')
{
*p-=32;

}
++p;

}
puts(buffer);
memset(buffer,0,256);

}

}

else
{
close(fd[0]);

int i=10;
while(i--)
{
write(fd[1],"hell0",5);

}

close(fd[1]);


}

exit(0);

}

结果:

有名管道

mkfifow.c

 int main()
{
  if(access("./fifo",F_OK)!=0)
 { 
    int res=mkfifo("./fifo",0666);
    assert(res!=-1);
 }
  puts("mkfifo  success...");
  int fdw=open("./fifo",O_WRONLY);
  assert(fdw!=-1);
  puts("w open success...");

  close(fdw);

}

mkfifor.c

 int main()
{
  if(access("./fifo",F_OK)!=0)
 { 
    int res=mkfifo("./fifo",0666);
    assert(res!=-1);
}
  puts("mkfifo  success...");
  int fdw=open("./fifo",O_RDONLY);
  assert(fdw!=-1);
  puts("r open success...");

  close(fdw);

}

access("./fifo",F_OK)//用于判断在当前目录中是否已经存在一个名字为fifo的文件,若有的话则返回0, mkfifo("./fifo",0666);//

注意:

若当该函数执行的时候,已经存在一个"./fifo"文件了,则会失败返回 -1;

注意:

和操作无名管道一样,只不过是在操作无名管道的时候我们不在显示的调用Open函数打开管道文件了。

切记:操作无名管道阻塞的位置是open而不是,read 或write位置处处。

在操作有名管道的时候: 如果以某种方式打开有名管道,则系统阻塞该进程,直到有另一个进程以另 一种方式打开管道后才会继续执行(注意:非阻塞标志O_NONBLOCK)都是在open处阻塞.

所以一个进程可以以可读可写的方式打开管道,则该进程不会阻塞

信号量的相关操作P操作V操作

实现进程进程之间的通信和控制

一:获取或是创建信号量

  int semget(key_t key,int num_sem,int sem_flags);

二:用于改变信号量的值

  int semop(int sem_id,struct sembuf* sem_ops,size_t num_sem_ops)

第一个参数是:信号量标示符 第二个参数是:指向下面结构体 第三个参数是: 信号量个数

    struct sembuf {
    short sem_num;
    short sem_op;//当P或V时         //取值不一样的  P 则              //sem_op=-1;   V 则
       //sem_op=1;
    short sem_flg;//一般取值
 //是 sem_flg=SEM_UNDO
   };

一般取值是 sem_flg=SEM_UNDO,含义是:
使得操作系统可以跟踪当前进程信号量的修改

三:用于控制信号量的

int semctl(int sem_id,int sem_num,int command,...)

第一个参数是:信号量标示符 第二个参数是:信号量标号(从0开始) 第三个参数是:将要采取的动作 有删除 IPC_RMID SETVAL 是把一个信号量设置为一个已知的值,是通过下面联合体中的val成员(一般是第一次使用时 即临界资源状态设置)

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

有关信号量的初始化:

注意下面是模拟 P V操作

#include<sys/sem.h>
int sem_init()
{
int semid=semget((key_t)12,1,0666|IPC_CREAT|IPC_EXCL);
if(semid==-1)
{
   semid=semget((key_t)12,1,0666);
  if(-1==semid)
{
perror("faill...");
return 0;
}
}

else
{
union semun em;
em.val=1;
if(semctl(semid,0,SETVAL,em)==-1)
{
return 0;
}
}
return 1;

}

int sem_p()
{
struct sembuf buf;
buf.sem_num=0;
buf.sem_op=-1;
buf.sem_flg=SEM_UNDO;

if(semop(semid,buf,1)==-1)
{
return 0;
}
return 1;

}

int sem_V()
{
struct sembuf buf;
buf.sem_num=0;
buf.sem_op=1;
buf.sem_flg=SEM_UNDO;

if(semop(semid,buf,1)==-1)
{
return 0;
}
return 1;

}

int sem_destroy()
{
  if(sem_ctl(semid,0,IPC_RMID)==-1)
{
return 0;
}
return 1;

}

有关共享类存的介绍

共享类存:是允许两个不相关的进程共享一个逻辑内存

#include<sys/shm.h>

一:用于建立共享内存

  int shmget(key_t key,size_t size,int shmflg);

第一个参数是:共享内存标示符
第二个参数是:共享内存大小
第三个参数是: 权限
//IPC_CREAT

二:用于链接共享内存

void* shmat(int shm_id,const void *shmaddr,int shmflg);

第一个参数是:共享内存标示符
第二个参数是:指定内存连接到当前的地址内存位置 一般给NULL表示让系统来选择共享内存出现的位置
第三个参数是: 权限 一般0

返回值:成功则返回共享内存的首地址
失败则返回-1

三:用于断开共享内存

int shmdt(const void *shmaddr);

第一个参数是:共享内存首地址

返回值:
成功则返回0
失败则返回-1

三:用于删除共享内存

int shmctl(int shm_id,int command,stuct chmid_ds *buf)

第一个参数是:共享内存标示符
第二个参数是:IPC_RMID
第三个参数是: NULL

int main()
{
int shmid=shmget((key_t)1234,256,0600|IPC_CREAT);
system("ipcs");

char*s=shmat(shmid,NULL,0);
system("ipcs");

shmdt(s);
system("ipcs");

shmctl(shmid,IPC_RMID,NULL);
system("ipcs");
}

消息队列的实现

消息队列提供了两个不相关进程之间的消息传递 是通过一个进程向一个存储队列中写东西 然后另一个进程就读数据

消息队列的最大长度是由一个宏 MSGMAX决定的

  该消息队列是独立于其进程的,即使进程都结束了 若没有进行删除操作就 仍然存在

 注意:

   这个与共享类存相似,内核为每个消息队列都设置了一个shmid_ds 结构体 用来管理消息队列

一:获取 或是 创建消息队列

struct mss
{
   long int type;//类型
   char buff[64];
};
  #include<sys/msg.h>
  int  msgget(key_t key ,int msgflg);

第一个参数: 用来标示消息队列的关键字
第二个参数: 标志位 如IPC_CREAT

返回值:成功返回一个标识符(非负值)失败返回-1

二:发送一个消息

 int msgsnd(int msgid ,void *msgp,size_t msgsize,int msgflg );

第一个参数: 由msgget返回的消息队列标示符
第二个参数: 指向要发送消息的缓冲区的指针
第三个参数: 表示消息长度(不包括长整形的大小)
第四个参数: 标志位 一般设置为0 表示当写满了就阻塞

返回值:成功返回0 失败返回-1

三:接收一个消息

 int msgrcv(int msgid ,void *msgp,size_t msgsize,long msgtype ,int msgflg );

第一个参数: 由msgget返回的消息队列标示符
第二个参数: 指向要接收消息的缓冲区的指针
第三个参数: 表示消息长度(不包括长整形的大小)
第四个参数:接收消息的类型 注意:当为0的时候便可以接收任何类型的

第五个参数: 标志位 一般设置为0 表示当写满了就阻塞

返回值:成功返回0 失败返回-1

四:删除一个消息队列

 int msgctl(int msgid,int cmd,struct msgid_ds *buf)

第一个参数: 由msgget返回的消息队列标示符

第二个参数: 采取的动作 IPC_RMID(删除)IPC_STAT(将内核的拷到buf)IPC_SET(将buf的拷到内核 权限足够)

第三个参数:缓冲区 一般为0

注意:消息队列的长度是65536

消息队列的结构体

 struct msgqid_ds
 {
    msgqnum_t msg_qnum;
    msglen_t msg_qbytes;//队列的大小
    pid_t msg_lspid;

 };

总结对比:

有关信号,管道,信号量,共享内存,消息队列的总结

从其效率看:共享内存最快,管道是所有的unix都支持的而消息队列 有些unix还不支持

欢迎一起交流学习

在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流

Thx

Author