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]);
fd[2]:用于返回文件描述符的数组
返回值:成功返回0,失败返回-1
注意: 都在内存中 如:
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],反之则相反)
1.当读一个写端已经被关闭的时候,在所有数据都读完后read返回0,指示到文件的结束
2.当写一个读端已经被关闭的时候,则产生SIGPIPE信号,如果忽略该信号或者捕获该信号并从其处理程序返回,则write返回出错,errno设置为EPIPE
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处阻塞.
所以一个进程可以以可读可写的方式打开管道,则该进程不会阻塞
临界资源:同一时刻,只允许一个进程访问的资源
临界区:访问临界资源的那段代码就是临区
原子操作:是指要么不做要么全做
实现进程进程之间的通信和控制
一:获取或是创建信号量
int semget(key_t key,int num_sem,int sem_flags);
第三个参数是:权限
sem_flags 取值: 0666|IPC_CREAT
一般IPC_CREAT与IPC_EXCL连起来运用 作用是确保此信号量是新建的
返回值:如果成功则返回信号量标示符(注意:是一个大于0的) 失败则返回-1
二:用于改变信号量的值
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;
};
有关信号,管道,信号量,共享内存,消息队列的总结
1:首先信号与信号量的区别:信号是用来进程间的通信(kill发送信号 signal接收信号)
而信号量是用与控制临界资源的访问
2:有关管道与共享内存以及消息队列的区别与联系
首先管道(是一种半双工通信)分为有名与无名管道,无名管道只可以用于有亲缘关系的进程通信(通常只有 一个读进程以及一个写进程)但是有名管道却可以有多个(可以用于两个无关的进程之间的通信),会产生一个fifo文件, 管道的实现原理:通过两个指针来控制一个缓冲区(是一个大小固定的4k)(还用到了信号和加锁机制)
3:消息队列与有名管道的却别:
4:共享内存方式是进程之间通信的最快方式,而消息队列以及管道都需要在内核和用户空间进行四次的数据拷贝
终结:
共享内存(是通过mmap()映射普通文件实现的)以及消息队列(其共享的区域的生存期不与通信进程结束相关)除非
显示的删除,否则是同内核持续的,而无名管道却不是这样的,但是有名管道却是持久的
从其效率看:共享内存最快,管道是所有的unix都支持的而消息队列 有些unix还不支持
在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流