Linux 进程间通讯
一、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 进程间通讯的更多相关文章
- Linux 进程间通讯方式 pipe()函数 (转载)
转自:http://blog.csdn.net/ta893115871/article/details/7478779 Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道( ...
- Linux 进程间通讯详解一
进程间的通讯 两台主机间的进程通讯 --socket 一台主机间的进程通讯 --管道(匿名管道,有名管道) --System V进程间通信(IPC)包括System V消息队列,System V信号量 ...
- linux进程间通讯-System V IPC 信号量
进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...
- Linux 进程间通讯详解二
消息队列 --消息队列提供了本机上从一个进程向另外一个进程发送一块数据的方法 --每个数据块都被认为有一个类型,接收者进程接收的数据块可以有不同的类型值 --消息队列也有管道一样的不足,就是每个消息的 ...
- Linux进程间通讯的几种方式的特点和优缺点,和适用场合
http://blog.csdn.net/jeffcjl/article/details/5523569 由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间 ...
- linux进程间通讯的几种方式的特点和优缺点
# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系.# 有名管道 (named pipe) : 有名管道也是 ...
- Linux 进程间通讯详解七
上图的一台主机服务器架构的重大缺陷是容易死锁 因为客户端,服务器都往同一消息队列中发送接收消息,假设消息队列已经满了,此时客户端无法向队列中发送消息,阻塞了,而服务器接收完一条消息后,想向消息队列发送 ...
- Linux 进程间通讯详解六
ftok()函数 key_t ftok(const char *pathname, int proj_id); --功能:创建系统建立IPC通讯 (消息队列.信号量和共享内存) 时key值 --参数 ...
- Linux 进程间通讯详解三
msgctl()函数 int msgctl(int msqid, int cmd, struct msqid_ds *buf); --参数 msqid:有msgget函数返回的消息队列标识码 cmd: ...
随机推荐
- 洛谷P3567 KUR-Couriers [POI2014] 主席树/莫队
正解:主席树/莫队 解题报告: 传送门! 这题好像就是个主席树板子题的样子,,,? 毕竟,主席树的最基本的功能就是,维护一段区间内某个数字的个数 但是毕竟是刚get到主席树,然后之前做的一直是第k大, ...
- qemu-kvm内存虚拟化1
2017-04-18 记得很早之前分析过KVM内部内存虚拟化的原理,仅仅知道KVM管理一个个slot并以此为基础转换GPA到HVA,却忽略了qemu端最初内存的申请,而今有时间借助于qemu源码分析下 ...
- LeetCode-111.Mininum Depth of Binary Tree
Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...
- Laravel展示产品-CRUD之show
上一篇讲了Laravel创建产品-CRUD之Create and Store,现在我们来做产品展示模块,用到是show,①首先我们先修改controller,文件是在/app/Http/Control ...
- ros 运行rviz时出现 QXcbConnection: XCB error: 148 错误 解决方法
出现上述问题的原因: 1.由于使用了nvc远程控制下位机: 2.rviz是一个基于opengl开发的图形插件,需要使用理论的屏幕参数(thetis' screen),由于使用了teamviewer会导 ...
- 火币网API文档——WebSocket API错误码
错误信息返回格式 { "id": "id generate by client", "status": "error", ...
- 20165236郭金涛 预备作业3 Linux安装及学习
我在Linux安装过程遇到的问题: 1.“不能为虚拟电脑XX打开一个新任务”: 出现这种情况是电脑没有开启blos,解决方法是:开机进入联想界面的时候,直接按F2可以快速进入选择开启blos. Lin ...
- python的开发语言介绍
-开发语言: 高级语言:python.java.c#.php.GO.ruby.c++ ===>字节码 低级语言:c.汇编 ===>机器码 语言之间的对比: PHP:适用于写网页, ...
- model browser 不出现时
1:当 创建 component 时, 创建完成后,没有出现model browser, 这时需要在model上面添加一个model,然后保存退出,重新进入,就会出现model browser
- 【Scrum】-NO.40.EBook.1.Scrum.1.001-【敏捷软件开发:原则、模式与实践】- Scrum
1.0.0 Summary Tittle:[Scrum]-NO.40.EBook.1.Scrum.1.001-[敏捷软件开发:原则.模式与实践]- Scrum Style:DesignPattern ...