首先想到的是使用环境变量。基本使用的是getenv()函数,获得相应的环境变量即可。在使用过程中,发现根本就不可能。因为环境变量是用户级别的,而每个console都相当于一个用户,不能进行环境变量沟通。
随后看到了IPC这个概念,遂查了一下,发现有管道、socket、信号、共享内存、消息队列等。
用到的管道和共享内存都是基于共享内存的。不得不说linux设计的太聪明了,膜拜这种抽象能力。程序中的文件抽象起来就是一块内存嘛,而文件又天生有共享的功能,伟大啊。
首先就是用shm,因为号称速度快。是在/dev/shm/下新建一个文件,这个文件夹是完全存在于内存中(有时会用到swap)的。不会有太多磁盘IO,所以会比较快。问题在于需要使用锁,才能保证读写的原子性。甚至是读到不完整的数据。所以不爱啊。主要就是shmget()、shmat()、shmctl()和shmdt()。锁则使用很相似的semget()、semctl()、semop()等。效果不错但是逻辑很复杂。
PV操作:
int p(int semid){
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
return semop(semid, &op, 1);
}
int v(int semid){
struct sembuf op;
op.sem_num = 0;
op.sem_op = 1;
return semop(semid, &op, 1);
}
Sender:
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>
#include"statics.h"
#include"pv.h"
int main(){
int shmid, semid;
key_t key;
message *msg;
if((key = ftok(PATH,ID))==-1){
printf("ftok error");
return -1;
}
if((shmid = shmget(key, 4096, IPC_CREAT)) == -1){
printf("shm error");
return -1;
}
if((msg = shmat(shmid,NULL,0)) == (message*)-1){
printf("shmat error");
return -1;
}
if((semid = semget(key, 1, IPC_CREAT)) == -1){
printf("sem error");
return -1;
}
union semun option;
option.val = 1;
semctl(semid, 0, SETVAL, option);
p(semid);
msg->type = 0;
msg->length = 1;
((int*)msg->data)[0] = 2;
v(semid);
shmdt(msg);
return 0;
}
Reciever:
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>
#include"statics.h"
#include"pv.h"
int main(){
int shmid, semid;
key_t key;
message *msg;
if((key = ftok(PATH,ID))==-1){
perror("ftok error");
return -1;
}
if((shmid = shmget(key, 4096, IPC_CREAT)) == -1){
perror("shm error");
return -1;
}
if((msg = shmat(shmid,NULL,0)) == (message*)-1){
perror("shmat error");
return -1;
}
if((semid = semget(key, 1, IPC_CREAT)) == -1){
perror("sem error");
return -1;
}
union semun option;
option.val = 1;
semctl(semid, 0, SETVAL, option);
while(1){
p(semid);
if(0 == msg->type){
int *arr = (int*)msg->data;
if(msg->length == 1){
int val = arr[0];
if(2 == val)
break;
}
}
v(semid);
sleep(1);
printf(".");
}
shmdt(msg);
printf("exit\n");
return 0;
}
还存在另一个问题,就是sem的释放时机和位置,因为双方的存在时间不一定,所以会有问题。
随后就是使用命名管道,命名管道是一个可见的文件,在运行结束后,管道是空的,文件大小也是0,。应该也是纯内存行为。命名管道主要是mkfifo()、open()、close()、read()、write()了,是文件的使用模式。虽然操作没有原子性(可能会写一部分,再写一部分数据),但是文件的锁机制应该是生效的。不会有读入旧数据的危险。使用起来也没显简单太多了。
Sender:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include"statics.h"
int main(){
message msg;
int file, count;
if((mkfifo(PATH, O_CREAT) < 0)&&(EEXIST != errno)){
perror("can't create fifo");
return -1;
}
file = open(PATH, O_WRONLY, 0);
if(-1 == file){
perror("file wrong");
}
msg.type = 0;
msg.length = 1;
((int*)msg.data)[0] = 2;
count = write(file, &msg, sizeof(msg));
if(-1 == count){
perror("can't write");
}
printf("count: %d\n", count);
close(file);
return 0;
}
Reciever:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include"statics.h"
int main(){
message msg;
int file, count;
if((mkfifo(PATH, O_CREAT) < 0)&&(EEXIST != errno)){
perror("can't create fifo");
return -1;
}
file = open(PATH, O_RDONLY|O_NONBLOCK, 0);
while(1){
sleep(1);
printf(".");
count = read(file, &msg, sizeof(msg));
if(-1 == count && EAGAIN == errno){
continue;
}else{
if(0 == msg.type){
int *arr = (int*)msg.data;
if(msg.length == 1){
int val = arr[0];
if(2 == val)
break;
}
}
}
}
printf("exit\n");
return 0;
}
问题又在于文件系统中会真的出现一个管道文件,大小比较美好。
过程中遇到了一些很小又有点意思的问题:
0.fork()函数,这个函数复制了栈的内容,也就是说把自己运行的空间复制了,导致会产生一次调用了两个函数的效果。很有意思,所以会返回两个值0子线程,val(>0)主线程。很有意思。而且整个的运行方式和MPI极像,终于明白MPI为什么会有那种不合理设计了。
1.printf()不输出,中间使用了printf('.')表征接受端的运行情况。但是基本不会有输出,直到进程结束。原因是printf是按行flush的,填满一行的点时间太长,所以会看不到一个点。解决方法有两个:使用stderr输出;fflush(stdout)强制flush。
2.头文件在make的时候多次include,出现多次定义的情况。解决办法是:在头文件中仅声明extern变量,另建一个c文件,专门定义变量。不需要加extern,但注意const等修饰要一致。
3.上面的程序需要root权限运行,后来发现,在mkfifo时,mode是可以指定pipe的访问权限的。使用0666就可以保证非root可用。
使用shm和fifo各进行10000次通信,看时间。是Reciever先运行,看sender完成时间。
shm | fifo |
453857 | 11906 |
6047 | 11827 |
5545 | 11697 |
7030 | 11019 |
457665 | 12766 |
18086 | 11820 |
AVG 158038.3333 | 11839.16667 |
VAR 53206115477 | 311377.3667 |
MID 12558 | 11823.5 |
换了另一个计时方法,发现shm的抖动是计时原因,换了另一个计时方法之后,shm也很稳定。速度也很快。也就是说shm性能还是比fifo稍好的。