进程间通信
一,管道,
管道的限制:
(1)半双工,数据只能在一个方向上流动
(2)管道一般只在具有公共祖先的进程之间使用,通常一个管道由一个进程创建,然后该进程调用fork()函数,此后父子进程可以使用该管道
管道的创建:
[cpp] view plaincopy
- #include <unistd.h>
- int pipe(int fileds[2]); //filedes[0]为读而打开,filedes[1]为写而打开
向一个没有读进程关联的管道写数据,会产生SIGPIPE,内核对于SIGPIPE的默认动作是退出该进程,这个通常不是我们期望看到的,因此我们需要重载这个信号处理方法,signal(SIGPIPE,SIG_IGN);
二,FIFO
FIFO被称为命名管道,和PIPE不同的是,通过FIFO不相关的进程也能交换数据
创建FIFO的方式如下
[cpp] view plaincopy
- int mkfifo(const char*pathname,mode_t mode);
在创建了fifo后,一般对文件的操作函数都可以用于它,open close read write等,
fifo被多个进程用于写很常见,所以应该考虑进程间的同步,并且多个写进程写的数据之间不会穿插。答:PIPE_BUF规定了内核中管道缓冲区的大小,所有写的进程写的大小必须要小于PIPE_BUF的大小
FIFO可以用于客户进程与服务进程之间的通信,如何保证同步?
答:服务进程创建一个众所周知的FIFO用来接收客户进程的请求,然后根据客户进程id不一样,再为每个客户进程创建一个fifo用来给客户进程发送服务进程的回应消息,这样的问题是服务进程如何知道客户进程已经退出了?
FIFO的缺点是不支持随机访问,因为PIPE和FIFO都是先入先出的特点
三,消息队列
查看linux系统共享内存和消息队列的情况:
ipcs [-m|-q|-s]
-m 输出有关共享内存(shared memory)的信息
-q 输出有关信息队列(message queue)的信息
ipcrm命令
用来手动解除linux使用的共享内存~
四,共享内存
以下共享内存大部分来自以下网址点击打开链接
共享内存适合比较大的数据集,因为它使用内存,支持快速的随机访问,也是最快的IPC形式共享内存并不是从某一进程拥有的内存中划分出来的;进程的内存总是私有的
shm_open():创建共享内存段或连接到现有的已命名内存段。这个系统调用返回一个文件描述符。
shm_unlink():根据(shm_open() 返回的)文件描述符,删除共享内存段。实际上,这个内存段直到访问
它的所有进程都退出时才会删除,这与在 UNIX 中删除文件很相似。但是,调用 shm_unlink() (通常由原来创建共享内存段的进程调用)之后,其他进程就无法访问这个内存段了。
mmap():把共享内存段映射到进程的内存。这个系统调用需要 shm_open() 返回的文件描述符,它返回指向内存的指针。(在某些情况下,还可以把一般文件或另一个设备的文件描述符映射到内存。mmap的实现也是基于上述原理,在使用mmap映射某个文件(或者文件的一部分)到进程的地址空间时,并没有加载文件的数据,而只是在进程的虚拟地址空间划分出一块区域,标记这块区域用于映射到文件的数据区域,mmap的操作就完成了。
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
munmap():作用与 mmap() 相反。
msync():用来让共享内存段与文件系统同步 — 当把文件映射到内存时,这种技术有用。
使用共享内存的步骤:
使用共享内存的过程是,用 shm_open() 创建内存段,用 write() 或 ftruncate() 设置它的大小,用 mmap() 把它映射到进程内存,执行其他参与者需要的操作。当使用完时,原来的进程调用 munmap() 和 shm_unlink(),然后退出。
示例程序
[cpp] view plaincopy
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/file.h>
- #include <sys/mman.h>
- #include <sys/wait.h>
- void error_and_die(const char *msg) {
- perror(msg);
- exit(EXIT_FAILURE);
- }
- int main(int argc, char *argv[]) {
- int r;
- const char *memname = "sample";
- const size_t region_size = sysconf(_SC_PAGE_SIZE);
- int fd = shm_open(memname, O_CREAT | O_TRUNC | O_RDWR, 0666);
- if (fd == -1)
- error_and_die("shm_open");
- r = ftruncate(fd, region_size);
- if (r != 0)
- error_and_die("ftruncate");
- void *ptr = mmap(0, region_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (ptr == MAP_FAILED)
- error_and_die("mmap");
- close(fd);
- pid_t pid = fork();
- if (pid == 0) {
- u_long *d = (u_long *) ptr;
- *d = 0xdbeebee;
- exit(0);
- }
- else {
- int status;
- waitpid(pid, &status, 0);
- printf("child wrote %#lx\n", *(u_long *) ptr);
- }
- r = munmap(ptr, region_size);
- if (r != 0)
- error_and_die("munmap");
- r = shm_unlink(memname);
- if (r != 0)
- error_and_die("shm_unlink");
- return 0;
- }
以下为另外一个示例程序
[cpp] view plaincopy
- /*-------------map_normalfile1.c-----------*/
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
- typedef struct{
- char name[4];
- int age;
- }people;
- main(int argc, char** argv) // map a normal file as shared mem:
- {
- int fd,i;
- people *p_map;
- char temp;
- fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
- lseek(fd,sizeof(people)*5-1,SEEK_SET);
- write(fd,"",1);
- p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
- MAP_SHARED,fd,0 );
- close( fd );
- temp = ‘a‘;
- for(i=0; i<10; i++)
- {
- temp += 1;
- memcpy( ( *(p_map+i) ).name, &temp,2 );
- ( *(p_map+i) ).age = 20+i;
- }
- printf(" initialize over \n ");
- sleep(10);
- munmap( p_map, sizeof(people)*10 );
- printf( "umap ok \n" );
- }
- /*-------------map_normalfile2.c-----------*/
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
- typedef struct{
- char name[4];
- int age;
- }people;
- main(int argc, char** argv) // map a normal file as shared mem:
- {
- int fd,i;
- people *p_map;
- fd=open( argv[1],O_CREAT|O_RDWR,00777 );
- p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
- MAP_SHARED,fd,0);
- for(i = 0;i<10;i++)
- {
- printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
- }
- munmap( p_map,sizeof(people)*10 );
- }
共享内存的限制:
正常大小为32M,可以通过shmmax更改~
/proc/sys/kernel/shmmax
五,信号
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。
信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。
信号处理函数如下
[cpp] view plaincopy
- void (*signal(int signo,void (*func)(int)))(int)
示例如下
[cpp] view plaincopy
- if(signal(SIGUSR1,sig_usr)==SIG_ERR)
- printf("can not catch SIG_USR1");
kill函数用来将信号发送给进程或者进程组,raise函数则允许进程向自身发送信号
[cpp] view plaincopy
- int kill(pid_t pid,int signo);
- int raise(int signo);
信号的缺点是:不能用来传输数据,一般用来在进程之间的异常通知
六:socket通信
七,文件
有点是可以共享大量的数据,缺点是共享速度慢,因为涉及到了磁盘的读写,磁盘的速度远比不上内存,另外文件本身不安全,有些特权用户可以将文件删除。