一、Linux 下进程间通讯方式

  1)管道(Pipe)及有名管道(named pipe):

  管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

  2)无名信号量(semaphore)级有名信号量(named semaphore):

  主要作为进程间以及同一进程不同线程之间的同步手段。

  3)信号(Signal)

  信号是比较复杂的通信方式,用于通知接受进程有某种事件生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期 信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上, 该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,sigaction函数重新实现了signal函数);

4)报文(Message)队列(消息队列)

  消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

 5)共享内存

  使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针其他通信机制运行效率较低设计的。往往与其它通信机制,如信号量结合使用, 来达到进程间的同步及互斥。

 6)套接字(Socket)

  更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix 系统上:Linux和System V的变种都支持套接字。

二、进程间通讯特点说明

1. socket
        1、使用socket通信的方式实现起来简单,可以使用因特网域和UNIX域来实现,使用因特网域可以实现不同主机之间的进出通信。
        2、该方式自身携带同步机制,不需要额外的方式来辅助实现同步。
        3、随进程持续。
2. 共享内存
        1、最快的一种通信方式,多个进程可同时访问同一片内存空间,相对其他方式来说具有更少的数据拷贝,效率较高。
        2、需要结合信号灯或其他方式来实现多个进程间同步,自身不具备同步机制。
        3、随内核持续,相比于随进程持续生命力更强。
3. 管道
        1、较早的一种通信方式,缺点明显:只能用于有亲缘关系进程之间的通信;只支持单向数据流,如果要双向通信需要多创建一个管道来实现。
        2、自身具备同步机制。
        3、随进程持续。
4. FIFO
        1、是有名管道,所以支持没有亲缘关系的进程通信。和共享内存类似,提供一个路径名字将各个无亲缘关系的进程关联起来。但是也需要创建两个描述符来实现双向通信。
        2、自身具备同步机制。
        3、随进程持续。
5. 信号
        1、这种通信可携带的信息极少。不适合需要经常携带数据的通信。
        2、不具备同步机制,类似于中断,什么时候产生信号,进程是不知道的。
6. 消息队列
        1、与共享内存和FIFO类似,使用一个路径名来实现各个无亲缘关系进程之间的通信。消息队列相比于其他方式有很多优点:它提供有格式的字节流,减少了开发人员的工作量;消息具有类型(system V)或优先级(posix)。其他方式都没有这些优点。
        1、具备同步机制。
        2、随内核持续。

三、进程间通讯实现方式

  1、有名管道(不相关的没有血缘关系的进程也可以相互通信)

、管道创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)); filname是指文件名,而mode是指定文件的读写权限。mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议用mkfifo。 open(const char *path, O_RDONLY);//
open(const char *path, O_RDONLY | O_NONBLOCK);//
open(const char *path, O_WRONLY);//
open(const char *path, O_WRONLY | O_NONBLOCK);//
、写入端代码
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h> int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -;
int data_fd = -;
int res = ;
const int open_mode = O_WRONLY;
int bytes_sent = ;
char buffer[PIPE_BUF + ];
int bytes_read = ; if(access(fifo_name, F_OK) == -)
{
printf ("Create the fifo pipe.\n");
res = mkfifo(fifo_name, ); if(res != )
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
} printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(fifo_name, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd); if(pipe_fd != -)
{
bytes_read = ;
data_fd = open("Data.txt", O_RDONLY);
if (data_fd == -)
{
close(pipe_fd);
fprintf (stderr, "Open file[Data.txt] failed\n");
return -;
} bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
while(bytes_read > )
{ res = write(pipe_fd, buffer, bytes_read);
if(res == -)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
} bytes_sent += res;
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
} close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE); printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
、 管道读取端
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h> int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -;
int data_fd = -;
int res = ;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + ];
int bytes_read = ;
int bytes_write = ; memset(buffer, '\0', sizeof(buffer)); printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(fifo_name, open_mode);
data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, ); if (data_fd == -)
{
fprintf(stderr, "Open file[DataFormFIFO.txt] failed\n");
close(pipe_fd);
return -;
} printf("Process %d result %d\n",getpid(), pipe_fd);
if(pipe_fd != -)
{
do
{
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_write = write(data_fd, buffer, res);
bytes_read += res;
}while(res > ); close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE); printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}

 2、无名管道(只能用于有血缘关系的进程之间,例如父子进程之间)

#include<unistd.h>
int pipe(pipefd[]);
pipefd[]//用来返回文件描述符的数组
pipefd[]//为读打开,为读端
pipefd[]//为写打开,为写端
//通常调用pipe()之后会调用fork进行创建父子进程,进行通信,对于管道数据的流向,要么父进程(写,关闭读端),子进程(读,关闭写端),要么子进程(写,关闭读端),父进程(读,关闭写端),在此之间,双方可以调用read(对于读进程)和write(对于写进程)对未关闭的文件描述符进行读写操作
//读写规则 1、读一个写端关闭的管道,在所有数据读完之后,read返回0,以指示文件到结尾处 2、如果写一个读端已关闭的管道,则产生SIGPIPE信号,捕捉信号write出错返回 3、互斥与原子性,在写的时候,读端不允许访问管道,并且已写尚未读取的字节数应该小于或等于PIPE_BUF(一般为4096字节)所规定的缓存大小
//当所有的读端与写端全部关闭后,管道才能被销毁
样例:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int pipefd[];//创建之后返回两个文件描述符pipefd[0]--读端 pipefd[2]--写端
pid_t cpid;
char buf;
if (pipe(pipefd) == -)//创建管道
{
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();//创建子进程
if (cpid<)
{
perror("fork cpid error");
exit(EXIT_FAILURE);
}
if (cpid == )//child
{
close(pipefd[]);//关闭文件描述符0,读端
int i = ;
char* _mesg = NULL;
while (i<)
{
_mesg = "hello father ! i am child.\n ";
write(pipefd[], _mesg, strlen(_mesg)+);
sleep();
i++;
}
}
else//father
{
close(pipefd[]);//父进程将argv[1]写入管道,关闭读端
char _mesg_f[];
int j = ;
while (j < )
{
memset(_mesg_f, '\0', sizeof(_mesg_f));
read(pipefd[], _mesg_f, sizeof(_mesg_f));
sleep();
printf("%s\n", _mesg_f);
j++;
}
}
return ;
}

 3、有名信号量

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag); //打开一个有名信号量,此时有名信号量是已经存在了的。
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);//创建有名信号量
参数说明:
name:信号量文件名。
flags:sem_open() 函数的行为标志。
mode:文件权限,可用八进制表示,如0777.
value:信号量初始值。 sem_close函数
#include <semaphore.h>
int sem_close(sem_t *sem);//关闭有名信号量
返回值:若成功,返回0;若出错,返回-
参数:
sem:指向信号量的指针。 sem_unlink函数
#include <semaphore.h>
int sem_unlink(const char *name);//删除有名信号量文件
返回值:若成功,返回0;若出错,返回-
参数: name:有名信号量文件名 sem_open 用于创建一个信号量,并能初始化它的值。
sem_wait 和 sem_trywait 相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。
sem_post   相当于 V 操作,它将信号量的值加一同时发出信号唤醒等待的进程。
sem_getvalue 获取信号量的值。
sem_close sem_unlink 删除信号量
#include <unistd.h>
#include <stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "name_sem_file"
int main ()
{
sem_t *sem = NULL;
//有名信号量在fork子程序中继承下来
//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量
sem = sem_open("name_sem", O_CREAT|O_RDWR, , ); //信号量值为 1
if(sem == SEM_FAILED)
{
perror("sem_open");
exit(-);
}
pid_t pid; //pid表示fork函数返回的值
int count=;
int fd = open(FILENAME,O_RDWR | O_CREAT,);
if(fd < )
{
perror("open");
}
if ((pid = fork()) < )
{
perror("fork");
exit(-);
}
else if (pid == )
{
char write_buf[] = "";
int i = ;
printf("child: pid = %ld,ppid = %ld, fd = %d, count = %d\n",(long)getpid(),(long)getppid(),fd,count);
for(i = ;i < ;i++)
{
/*信号量减一,P 操作*/
sem_wait(sem);
for(i = ;i<;i++)
{
if (write(fd,write_buf,sizeof(write_buf)))
{
perror("write");
}
count++;
}
printf("in child....and count = %d\n",count);
/*信号量加一,V 操作*/
sem_post(sem);
sleep();
}
exit();
}
else
{
char write_buf[] = "";
int i = ;
printf("parent: pid = %ld,ppid = %ld, fd = %d, count = %d\n",(long)getpid(),(long)getppid(),fd,count);
for(i = ; i<; i++)
{
/*信号量减一,P 操作*/
sem_wait(sem);
for(i = ;i<;i++)
{
if (write(fd,write_buf,sizeof(write_buf)))
{
perror("write");
}
count++;
}
printf("in father.... count = %d\n",count);
/*信号量加一,V 操作*/
sem_post(sem);
sleep();
}
printf("Waiting for the child process to exit\n");
//等待子进程退出
waitpid(pid,NULL,);
sem_del("name_sem"); //删除信号量文件 name_sem
exit(); }
//return 0;
}
#include <unistd.h>
#include <stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "sync_name_sem_file" int main ()
{
sem_t *sem_1 = NULL;
sem_t *sem_2 = NULL;
//有名信号量在fork子程序中继承下来
//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量
sem_1 = sem_open("sync_name_sem_1", O_CREAT|O_RDWR, , ); //信号量值为 0
sem_2 = sem_open("sync_name_sem_2", O_CREAT|O_RDWR, , ); //信号量值为 1
if( (sem_1 == SEM_FAILED) | (sem_2 == SEM_FAILED))
{
perror("sem_open");
exit(-);
}
pid_t pid; //pid表示fork函数返回的值
int count=;
int fd = open(FILENAME,O_RDWR | O_CREAT,);
if(fd < )
{
perror("open");
}
if ((pid = fork()) < )
{
perror("fork");
exit(-);
}
else if (pid == )
{
char write_buf[] = "";
int i = ;
printf("child: pid = %ld,ppid = %ld, fd = %d, count = %d\n",(long)getpid(),(long)getppid(),fd,count);
for(i = ;i<;i++)
{
/*信号量减一,P 操作*/
sem_wait(sem_1);
for(i = ;i<;i++)
{
if (write(fd,write_buf,sizeof(write_buf)))
{
perror("write");
}
count++;
}
printf("in child....and count = %d\n",count);
/*信号量加一,V 操作*/
sem_post(sem_2);
sleep()
}
exit();
}
else
{
char write_buf[] = "";
int i = ;
printf("parent: pid = %ld,ppid = %ld, fd = %d, count = %d\n",(long)getpid(),(long)getppid(),fd,count);
for(i = ;i<;i++)
{
/*信号量减一,P 操作*/
sem_wait(sem_2);
for(i = ;i<;i++)
{
if (write(fd,write_buf,sizeof(write_buf)))
{
perror("write");
}
count++;
}
printf("in father.... count = %d\n",count);
/*信号量加一,V 操作*/
sem_post(sem_1);
sleep();
}
printf("Waiting for the child process to exit\n");
//等待子进程退出
waitpid(pid,NULL,);
sem_del("sync_name_sem_1"); //删除信号量文件 sync_name_sem_1
sem_del("sync_name_sem_2"); //删除信号量文件 sync_name_sem_2
exit();
}
//return 0;
}

4、无名信号量 

)初始化创建信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem:信号量的地址。
pshared:等于 ,信号量在线程间共享(常用);不等于0,信号量在进程间共享。
value:信号量的初始值
)信号量 P 操作(减 )
int sem_wait(sem_t *sem);
参数:
sem:信号量的地址。
int sem_trywait(sem_t *sem); //以非阻塞的方式来对信号量进行减 1 操作。若操作前,信号量的值等于 0,则对信号量的操作失败,函数立即返回。
)信号量 V 操作(加 )
int sem_post(sem_t *sem);
参数:
sem:信号量的地址。
)获取信号量的值
int sem_getvalue(sem_t *sem, int *sval);
参数:
sem:信号量地址。
sval:保存信号量值的地址。
)销毁信号量
int sem_destroy(sem_t *sem);
参数:
sem:信号量地址。
信号量用于互斥实例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h> sem_t sem; //信号量 void printer(char *str)
{
sem_wait(&sem);//减一
while(*str)
{
putchar(*str);
fflush(stdout);
str++;
sleep();
}
printf("\n"); sem_post(&sem);//加一
} void *thread_fun1(void *arg)
{
char *str1 = "hello";
printer(str1);
} void *thread_fun2(void *arg)
{
char *str2 = "world";
printer(str2);
} int main(void)
{
pthread_t tid1, tid2; sem_init(&sem, , ); //初始化信号量,初始值为 1 //创建 2 个线程
pthread_create(&tid1, NULL, thread_fun1, NULL);
pthread_create(&tid2, NULL, thread_fun2, NULL); //等待线程结束,回收其资源
pthread_join(tid1, NULL);
pthread_join(tid2, NULL); sem_destroy(&sem); //销毁信号量 return ;
}
信号量用于同步实例:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h> sem_t sem_g,sem_p; //定义两个信号量
char ch = 'a'; void *pthread_g(void *arg) //此线程改变字符ch的值
{
while()
{
sem_wait(&sem_g);
ch++;
sleep();
sem_post(&sem_p);
}
} void *pthread_p(void *arg) //此线程打印ch的值
{
while()
{
sem_wait(&sem_p);
printf("%c",ch);
fflush(stdout);
sem_post(&sem_g);
}
} int main(int argc, char *argv[])
{
pthread_t tid1,tid2;
sem_init(&sem_g, , ); //初始化信号量
sem_init(&sem_p, , ); pthread_create(&tid1, NULL, pthread_g, NULL);
pthread_create(&tid2, NULL, pthread_p, NULL); pthread_join(tid1, NULL);
pthread_join(tid2, NULL); return ;
}

5、信号

SIGHUP  A 终端挂起或者控制进程终止

SIGINT  A 键盘中断(如break键被按下)

SIGQUIT  C 键盘的退出键被按下

SIGILL  C 非法指令

SIGABRT  C 由abort()发出的退出指令

SIGFPE  C 浮点异常

SIGKILL  AEF Kill信号

SIGSEGV  C 无效的内存引用

SIGPIPE  A 管道破裂: 写一个没有读端口的管道

SIGALRM  A 由alarm()发出的信号

SIGTERM  A 终止信号

SIGUSR1 ,, A 用户自定义信号1

SIGUSR2 ,, A 用户自定义信号2

SIGCHLD ,, B 子进程结束信号

SIGCONT ,, 进程继续(曾被停止的进程)

SIGSTOP ,, DEF 终止进程

SIGTSTP ,, D 控制终端(tty)上按下停止键

SIGTTIN ,, D 后台进程企图从控制终端读

SIGTTOU ,, D 后台进程企图从控制终端写
样例:
void sigroutine(int signo) { switch (signo) { case SIGALRM: printf("Catch a signal -- SIGALRM "); break; case SIGVTALRM: printf("Catch a signal -- SIGVTALRM "); break; } return; } int main() { struct itimerval value,ovalue,value2; sec = ; printf("process id is %d ",getpid()); signal(SIGALRM, sigroutine); signal(SIGVTALRM, sigroutine); value.it_value.tv_sec = ; value.it_value.tv_usec = ; value.it_interval.tv_sec = ; value.it_interval.tv_usec = ; setitimer(ITIMER_REAL, &value, &ovalue); value2.it_value.tv_sec = ; value2.it_value.tv_usec = ; value2.it_interval.tv_sec = ; value2.it_interval.tv_usec = ; setitimer(ITIMER_VIRTUAL, &value2, &ovalue); for (;;) ; }

6、共享内存

、shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
函数说明:得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符。
参数:
key ftok函数返回的I P C键值
size 大于0的整数,新建的共享内存大小,以字节为单位,获取已存在的共享内存块标识符时,该参数为0,
shmflg IPC_CREAT||IPC_EXCL 执行成功,保证返回一个新的共享内存标识符,附加参数指定IPC对象存储权限,如|
返回值:成功返回共享内存的标识符,出错返回-,并设置error错误位。 、shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void shmaddr, int shmflg); 函数说明:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间
参数:
shmid: 共享内存标识符
shmaddr: 指定共享内存出现在进程内存地址的什么位置,通常指定为NULL,让内核自己选择一个合适的地址位置
shmflg: SHM_RDONLY 为只读模式,其他参数为读写模式
返回值:成功返回附加好的共享内存地址,出错返回-,并设置error错误位 、shmdt
#include <sys/types.h>
#include <sys/shm.h>
void *shmdt(const void* shmaddr); 函数说明:与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存,需要注意的是,该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程
参数:shmddr 连接共享内存的起始地址
返回值:成功返回0,出错返回-,并设置error。 、shmctl
#include <sys/types.h>
#Include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
函数说明:控制共享内存块
参数:
shmid:共享内存标识符
cmd:
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构赋值到buf所指向的buf中
IPC_SET:改变共享内存的状态,把buf所指向的shmid_ds结构中的uid、gid、mode赋值到共享内存的shmid_ds结构内
IPC_RMID:删除这块共享内存
buf:共享内存管理结构体
返回值:成功返回0,出错返回-,并设置error错误位。
comm.h

#ifndef _COMM_H_
#define _COMM_H_ #include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "." // ftok函数 生成key使用
#define PROJ_ID 66 // ftok 函数生成key使用
int create_shm( int size);// 分配指定大小的共享内存块
int destroy_shm( int shmid); // 释放指定id的共享内存块
int get_shmid(); // 获取已经存在的共享内存块 #endif /*_COMM_H_*/
#include "comm.h"

//
static int comm_shm(int size, int shmflag)
{
key_t key = ftok(PATHNAME, PROJ_ID); // 获取key
if(key < ){
perror("ftok");
return -;
}
int shmid = shmget(key, size, shmflag);
if(shmid < ){
perror("shmget");
return -;
}
return shmid;
}
int create_shm( int size)
{
return comm_shm(size, IPC_CREAT|IPC_EXCL|);
} int get_shmid()
{
return comm_shm(, IPC_CREAT);
}
int destroy_shm(int shmid)
{
if( shmctl( shmid, IPC_RMID, NULL) < )
{
perror("shmctl");
return -;
}
return ;
}
server.c

#include "comm.h"

int main()
{
int shmid = create_shm();// 创建共享内存块
char *buf;
int i = ;
buf = shmat(shmid,NULL, );
while( i < )
{
buf[i] = 'a'+i ;
i++;
sleep();
if(i == )
break; // 让程序结束,去释放该共享内存
}
destroy_shm(shmid);
return ;
}
clinet.c

#include "comm.h"

int main()
{
int shmid = get_shmid();
char *buf;
int index = ;
buf = shmat(shmid,NULL, );
while( index < )
{
printf("%s\n", buf);
sleep();
index++;
if( index == )
break; // 让程序结束
}
return ;
}

7、消息队列

函数功能介绍
msgget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg); 功能:创建或取得一个消息队列对象
返回:消息队列对象的id 同一个key得到同一个对象
格式:msgget(key,flag|mode);
flag:可以是0或者IPC_CREAT(不存在就创建)
mode:同文件权限一样 msgsnd函数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:将msgp消息写入标识为msgid的消息队列
msgp:
struct msgbuf {
long mtype; /* message type, must be > 0 */消息的类型必须>
char mtext[]; /* message data */长度随意
}; msgsz:要发送的消息的大小 不包括消息的类型占用的4个字节
msgflg: 如果是0 当消息队列为满 msgsnd会阻塞
如果是IPC_NOWAIT 当消息队列为满时 不阻塞 立即返回
返回值:成功返回id 失败返回- msgrcv函数
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:从标识符为msgid的消息队列里接收一个指定类型的消息 并 存储于msgp中 读取后 把消息从消息队列中删除
msgtyp:为 表示无论什么类型 都可以接收
msgp:存放消息的结构体
msgsz:要接收的消息的大小 不包含消息类型占用的4字节
msgflg:如果是0 标识如果没有指定类型的消息 就一直等待
如果是IPC_NOWAIT 则表示不等待 msgctl函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgctl(msgid,IPC_RMID,NULL);//删除消息队列对象
接收模块

/*=============================================================================
# FileName: msgreceive.c
# Desc: receice message from message queue
# Author: Licaibiao
# Version:
# LastChange: 2017-01-20
# History:
=============================================================================*/ #include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h> struct msg_st
{
long int msg_type;
char text[];
}; int main()
{
int running = ;
int msgid = -;
int len = ;
long int msgtype = ;
struct msg_st data; msgid = msgget((key_t), | IPC_CREAT);
if(- == msgid )
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
} while(running)
{
memset(&data.text, , );
len = msgrcv(msgid, (void*)&data, , msgtype, );
if(- == len)
{
fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s\n",data.text); if( == strncmp(data.text, "end", ))
running = ;
} //remove message queue
if(- == msgctl(msgid, IPC_RMID, ))
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
发送模块
/*=============================================================================
# FileName: msgsend.c
# Desc: send data to message queue
# Author: Licaibiao
# Version:
# LastChange: 2017-01-20
# History:
=============================================================================*/ #include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h> #define MAX_TEXT 512
struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
}; int main()
{
int running = ;
struct msg_st data;
char buffer[BUFSIZ];
int msgid = -;
int len; msgid = msgget((key_t), | IPC_CREAT);
if(msgid == -)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
} while(running)
{ printf("Enter data : ");
fgets(buffer, BUFSIZ, stdin);
data.msg_type = ;
strcpy(data.text, buffer);
len = strlen(data.text);
if(msgsnd(msgid, (void*)&data, len-, ) == -)
{
fprintf(stderr, "msgsnd failed\n");
exit(EXIT_FAILURE);
} if(strncmp(buffer, "end", ) == )
running = ;
usleep();
}
exit(EXIT_SUCCESS);
}

 三、补充

  1、多进程与多线程区别

对比维度 多进程 多线程 总结
数据共享、同步 数据共享复杂,需要用IPC;数据是分开的,同步简单 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 各有优势
内存、CPU 占用内存多,切换复杂,CPU利用率低 占用内存少,切换简单,CPU利用率高 线程占优
创建销毁、切换 创建销毁、切换复杂,速度慢  创建销毁、切换简单,速度很快  线程占优
 编程、调试  编程简单,调试简单  编程复杂,调试复杂  进程占优
 可靠性  进程间不会互相影响  一个线程挂掉将导致整个进程挂掉  进程占优
 分布式  适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单

  

Linux 进程间通讯的更多相关文章

  1. Linux 进程间通讯方式 pipe()函数 (转载)

    转自:http://blog.csdn.net/ta893115871/article/details/7478779 Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道( ...

  2. Linux 进程间通讯详解一

    进程间的通讯 两台主机间的进程通讯 --socket 一台主机间的进程通讯 --管道(匿名管道,有名管道) --System V进程间通信(IPC)包括System V消息队列,System V信号量 ...

  3. linux进程间通讯-System V IPC 信号量

    进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...

  4. Linux 进程间通讯详解二

    消息队列 --消息队列提供了本机上从一个进程向另外一个进程发送一块数据的方法 --每个数据块都被认为有一个类型,接收者进程接收的数据块可以有不同的类型值 --消息队列也有管道一样的不足,就是每个消息的 ...

  5. Linux进程间通讯的几种方式的特点和优缺点,和适用场合

    http://blog.csdn.net/jeffcjl/article/details/5523569 由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间 ...

  6. linux进程间通讯的几种方式的特点和优缺点

    # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系.# 有名管道 (named pipe) : 有名管道也是 ...

  7. Linux 进程间通讯详解七

    上图的一台主机服务器架构的重大缺陷是容易死锁 因为客户端,服务器都往同一消息队列中发送接收消息,假设消息队列已经满了,此时客户端无法向队列中发送消息,阻塞了,而服务器接收完一条消息后,想向消息队列发送 ...

  8. Linux 进程间通讯详解六

    ftok()函数 key_t ftok(const char *pathname, int proj_id); --功能:创建系统建立IPC通讯 (消息队列.信号量和共享内存) 时key值 --参数 ...

  9. Linux 进程间通讯详解三

    msgctl()函数 int msgctl(int msqid, int cmd, struct msqid_ds *buf); --参数 msqid:有msgget函数返回的消息队列标识码 cmd: ...

随机推荐

  1. LeetCode 657 Robot Return to Origin 解题报告

    题目要求 There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of it ...

  2. 《HTTP - https》

    一:HTTP 缺点? - 明文通讯(也是最受诟病的一个缺点) - 不验证对方的身份(你说你是你?你怎么证明你是你呢?) - 无法验证报文的完整性,可能已经被篡改(在挨打的边缘,来回试探) 二:HTTP ...

  3. 递归、嵌套for循环、map集合方式实现树形结构菜单列表查询

    有时候, 我们需要用到菜单列表,但是怎么样去实现一个菜单列表的编写呢,这是一重要的问题. 比如我们需要编写一个树形结构的菜单,那么我们可以使用JQuery的zTree插件:http://www.tre ...

  4. [转载]MACD 各周期指标状态

    MACD指标:MACD指标是一个非常好用的指标,它与均线.量价关系配合使用对判断行情很有效.这里有必要再深一点讲MACD级别之间的作用. 一.首先,必须明白的是任何指标中都是大级别包含小级别,小级别对 ...

  5. 使用python脚本实现统计日志文件中的ip访问次数

    使用python脚本实现统计日志文件中的ip访问次数,注意此脚本只适用ip在每行开头的日志文件,需要的朋友可以参考下 适用的日志格式: 106.45.185.214 - - [06/Aug/2014: ...

  6. Mac搭建PHP+rabbitMQ环境

    RabbitMQ是一个在AMQP基础上实现的企业级消息系统.何谓消息系统,就是消息队列系统,消息队列是“”消费-生产者模型“”的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅 ...

  7. Eclipse中git上如何把自己的分支保存到远端

    1   Team——>remote——>push 2  next 3  选择自己的分支,然后点击 Add Spec 4  查看是否是自己的分支——>自己的分支,然后Finish PS ...

  8. Java 基础 变量和运算符

    Java基础语法   第1章 变量 1.1 变量概述 1.2 计算机存储单元 1.3 基本类型之4类8种 1.4 常量与类型 1.5 定义变量(创建变量) 1.6 变量使用的注意事项 1.7 数据类型 ...

  9. 【LeetCode每天一题】Swap Nodes in Pairs

    Given a linked list, swap every two adjacent nodes and return its head. You may not modify the value ...

  10. MyBatis SpringMVC映射配置注意

    applicationContext.xml中要配置 <!-- MyBatis 的 sqlSessionFactory --> <bean id="sqlSessionFa ...