LinuxIPC,FIFO和shm
LinuxIPC,FIFO和shm
偶尔发现了调试函数需要的一个功能:根据反馈的信息,调整一些简单的参数。有两个要求:1、不中断程序,又不会每次都需要暂停等待输入;2、不需要重新编译,程序运行。这样需求基本上就是多进程、监听加IPC就ok。
首先想到的是使用环境变量。基本使用的是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等修饰要一致。
明天做一下速度测试,还没想好方法。思考ing...
评论暂时关闭