linux-IPCs
Linux进程间通信方式汇总
目前已包含的方式
- 管道(PIPE) 无名管道
- FIFO(有名管道)
- XSI消息队列
- XSI信号量
- XSI共享内存
- POSIX信号量
- 域套接字(Domain Socket)
- 信号(Signal)
- 互斥量(Mutex)
- sendmsg
其中信号(signal)和信号量(semaphore)本质上并不算是进程间通信方式,应该是进程间同步的方式,但是也可以起到一定的通信作用,故也列在上面。
另外普通的mutex是作用线程间同步用的,但是可以将进程A和进程B共享的内存中初始化一个mutex,这样就可以用将此mutex用作进程间通信用了。
无名管道 Pipe
同主机进程间数据交互机制:
- 无名管道(PIPE):只有创建该管道的程序,才能够找到该管道(亲缘进程通信)
- 单向传输
- 控制进程只有pipe的一端
- pipe的阻塞操作
*fd[0] 读 fd[1]写
SIGPIPE - 无名管道的内核资源在通信两进程退出后会自动释放,不能像普通文件一样存储信息,不能使用lseek来修改当前pipe的使用位置
read write 返回-1
会收到sigpipe信号
Demo
#include<stdio.h>
#include<errno.h>
#include<sys/wait.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
int main()
{
int pipe_fd[2] = {-1,-1};
if(pipe(pipe_fd) < 0){
printf("pipe error, %s\n",strerror(errno));
return 1;
}
pid_t id = fork();
if(id == 0){
//child write
close(pipe_fd[0]);
char *msg = "child write:enenshiwo\n";
while(1){
write(pipe_fd[1],msg,strlen(msg));
sleep(1);
}
}else{
//father read
close(pipe_fd[1]);
char buf[1024];
while(1){
buf[0] = '\0';
char *msg_f = "father write:enenshiwo\n";
ssize_t _sz = read(pipe_fd[0],buf,sizeof(buf) - 1);
if(_sz > 0){
buf[_sz] = '\0';
}
printf("father read : %s\n",buf);
}
wait(NULL);
}
return 0;
}
有名管道 fifo
- FIFO 是依赖于文件系统,
依赖于文件系统,像普通文件一样具有磁盘路径,文件权限和其他属性,所有程序都可以通过path找到有名管道
- fifo是一个文件,存储信息在内存,当两个进程都消失,数据消息,文件只有接口的作用
- mkfifo
int mkfifo(const char* pathname, mode_t mode); - 单项通信
- 只能够进行少量数据传输
- 只能一对一进行传输
- 两个进程,中途退出了一个进程,未退出的一端如果是写操作的话,返回sigpipe信号
- 未退出的一端如果是阻塞读操作的话,该读操作不会继续阻塞,直接返回0
Demo
fifo_write.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc,char *argv[])
{
int pipe_fd;
int res;
char buffer[]="hello world!";
if (access(FIFO_NAME, F_OK) == -1)
{
res = mkfifo(FIFO_NAME, 0766);
if (res != 0)
{
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, O_WRONLY);
printf("the file's descriptor is %d\n", pipe_fd);
if (pipe_fd != -1)
{
res = write(pipe_fd, buffer, sizeof(buffer));
if (res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
printf("write data is %s,%d bytes is wirte\n",buffer,res);
(void)close(pipe_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
fifo_read.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc,char *argv[])
{
int pipe_fd;
int res;
char buffer[4096];
int bytes_read = 0;
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY);
printf("the file's descriptor is %d\n",pipe_fd);
if (pipe_fd != -1)
{
bytes_read = read(pipe_fd, buffer, sizeof(buffer));
printf("the read data is %s\n",buffer);
close(pipe_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
system v ipc
ipcs
- key:通信双方约定好的KEY值,32位的整形数据
- id :系统会为IPC分配一个唯一的ID,通信双方都需要知道ID才能使用当前IPC方法,创建者会返回当前ID值,
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
. proj_id
key 31-24 proj_id 低8位
key 23-16 st_dev属性的低8位
key 15-0 st_ino属性的低16位
- 对象
- 拥有者及权限对象
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
system v ipc
ipcs
- key:通信双方约定好的KEY值,32位的整形数据
- id :系统会为IPC分配一个唯一的ID,通信双方都需要知道ID才能使用当前IPC方法,创建者会返回当前ID值,
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
. proj_id
key 31-24 proj_id 低8位
key 23-16 st_dev属性的低8位
key 15-0 st_ino属性的低16位
- 对象
- 拥有者及权限对象
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
XSI消息队列
- 对象:
struct msqid_ds
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
struct msg * msq_first ;
struct msg * msq_last ;
};
优点和缺点
优点:
1. 消息队列是两个不相关进程之间传递数据的有效渠道.
2. 与命名管道相比,具有的优势是:
独立于发送以及接收进程而存在;
消除命名管道的打开以及关闭而存在的困难;
可以提前查看紧急信息;
避免命名管道的同步以及阻塞问题;
缺点:
与命名管道一样,每个数据块都有一个最大长度限制;
系统中所有队列包含的全部数据块长度也有一个上限;
- 创建对象:
int msgget(key_t key, int msgflg);
int msgget(key_t key, int msgflg);
key_t key
int msgflg 确定消息队列的访问权限,
IPC_CREAT 找到当前的key对应的IPC,如果没有IPC则进行创建
IPC_EXCL 如果存在则返回失败
IPC_NOWAIT 返回值为当前ipc对应的id值
- 设置对象:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
cmd:
IPC_RMID 删除
IPC_SET 设置ipc_perm参数
IPC_STAT 获取ipc_perm参数
IPC_INFO 获取ipc信息 和ipcs
-
使用对象:
- 发送
int msgsnd(int msqid,const void *msg_ptr,size_t sz,int msgflg); struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ };
- 接收
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msgtyp 0 默认接收第一条消息
msgtyp > 0 接收第一条msgtyp消息
msgtyp < 0 接收小于msgtyp绝对值的消息
早期的版本,现在根据Ubuntu的版本不一样可能会有出入
msgmni 最大消息队列数 16
msgmax 最大消息长度(字节数) 8192
msgmnb 消息队列中的最大字节数 16384
demo
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<string.h>
#include<sys/msg.h>
#define BUFSIZE 128
struct msg_buf
{
long type;
char msg[BUFSIZE];
};
int main(int argc,char *argv[])
{
key_t key;
int msgid;
struct msg_buf msg_snd,msg_rcv;
struct msginfo buf;
char *ptr="helloworld";
memset(&msg_snd,'\0',sizeof(struct msg_buf));
memset(&msg_rcv,'\0',sizeof(struct msg_buf));
msg_rcv.type=1;
msg_snd.type=1;
memcpy(msg_snd.msg,ptr,strlen(ptr));
if((key=ftok(".",'A'))==-1)
{
perror("ftok");
exit(EXIT_FAILURE);
}
if((msgid=msgget(key,0600|IPC_CREAT))==-1)
{
perror("msgget");
exit(EXIT_FAILURE);
}
printf("msgsnd_return=%d\n",msgsnd(msgid,(void *)&msg_snd,strlen(msg_snd.msg),0));
msgctl(msgid,MSG_INFO,&buf);
printf("buf.msgmax=%d\n",buf.msgmax);
printf("buf.msgmnb=%d\n",buf.msgmnb);
printf("buf.msgpool=%d\n",buf.msgpool);
printf("buf.semmap=%d\n",buf.msgmap);
printf("buf.msgmni=%d\n",buf.msgmni);
printf("buf.msgssz=%d\n",buf.msgssz);
printf("buf.msgtql=%d\n",buf.msgtql);
printf("buf.msgseg=%u\n",buf.msgseg);
printf("msgrcv_return=%d\n",msgrcv(msgid,(void *)&msg_rcv,BUFSIZE,msg_rcv.type,0));
printf("rev msg:%s\n",msg_rcv.msg);
printf("msgctl_return=%d\n",msgctl(msgid,IPC_RMID,0));
}
XSI信号量
概念:
- 实现进程间同步
- 表示系统可用资源的个数
对象:
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
struct sem * sem_base
};
struct sem{
int semval 信号量的值
int sempid 最近一个操作的进程号
}
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
对象操作
- 创建对象:
int semget(key_t k,int n,int semflag);
- 设置对象:
int semctl(int sid,int semnum,int cmd,union semun a);
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short* array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
cmd 如果对于整个信号量集合,则有如下设置方法
IPC_STAT ipc_perm
IPC_SET ipc_perm
IPC_RMID
IPC_INFO 信号量的系统信息
int semctl(int sid,int semnum,int cmd,…);
cmd 如果对于单个信号量集合,则有如下设置方法
GETPID 返回操作IPC的PID
GETVAL 返回操作IPC的VAL
GETALL返回操作IPC的所有值
SETVAL 设置操作IPC的VAL
SETALL 设置操作IPC的所有值
- 使用对象
int semop(int s,struct sembuf *sb,size _t n);
struct sembuf{
unsigned short int sem_num;
short int sem_op;
short int sem_flg;};
sem_op
+ 表示进行加
- 表示进行减
0 表示测试是否为0
- 如果当前信号量的操作后的值为负数,则进行阻塞,一直到满足条件为止
sem_flg:
IPC_NOWAIT 如果操作信号集合中任意一个失败,立即返回,并且不会对其他的信号量做操作
SEM_UNDO :进程退出后,该进程对sem进行的操作将被撤销
struct sembuf sops[4]
sops[0].sem_num =1
sops[0].sem_op =-1 / P操作/
sops[0].sem_flag = 0
sops[1].sem_num =2
sops[1].sem_op =1 / V操作/
sops[1].sem_flag =0
demo
生产者:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;
void init()
{
key_t key;
int ret;
unsigned short sem_array[2];
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}arg;
key=ftok(".",'s');
sem_id=semget(key,2,IPC_CREAT|0644);
sem_array[0]=0; //identify the productor
sem_array[1]=100; //identify the space
//printf("set the productor init value is 0\nset the space init value is 100\n");
arg.array = sem_array;
ret = semctl(sem_id, 0, SETALL, arg);
if (ret == -1)
printf("SETALL failed (%d)\n", errno);
//printf("\nread the number\n");
printf("productor init is %d\n",semctl(sem_id,0,GETVAL));
printf("space init is %d\n\n",semctl(sem_id,1,GETVAL));
}
void del()
{
semctl(sem_id,IPC_RMID,0);
}
#define produce 1
#define customer 0
#define P 1
#define V 0
int main(int argc,char *argv[])
{
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = -1;
sops[1].sem_flg = 0;
init();
printf("this is productor\n");
while(1)
{
printf("\n\nbefore produce:\n");
printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
printf("space number is %d\n",semctl(sem_id,1,GETVAL));
//生产者的操作
semop(sem_id,(struct sembuf *)&sops[P],produce); //get the space to instore the productor
printf("now producing......\n");
semop(sem_id,(struct sembuf *)&sops[V],customer); //now tell the customer can bu cusume
printf("\nafter produce\n");
printf("spaces number is %d\n",semctl(sem_id,1,GETVAL));
printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
sleep(4);
}
del();
}
消费者:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;
void init()
{
key_t key;
key=ftok(".",'s');
sem_id=semget(key,2,IPC_CREAT|0644);
//printf("sem id is %d\n",sem_id);
}
#define produce 1
#define customer 0
#define P 1
#define V 0
int main(int argc,char *argv[])
{
init();
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = -1;
sops[1].sem_flg = 0;
printf("this is customer\n");
while(1)
{
printf("\n\nbefore consume:\n");
printf("productor is %d\n",semctl(sem_id,0,GETVAL));
printf("space is %d\n",semctl(sem_id,1,GETVAL));
semop(sem_id,(struct sembuf *)&sops[P],customer); //get the productor to cusume
printf("now consuming......\n");
semop(sem_id,(struct sembuf *)&sops[V],produce); //now tell the productor can bu produce
printf("\nafter consume\n");
printf("products number is %d\n",semctl(sem_id,0,GETVAL));
printf("space number is %d\n",semctl(sem_id,1,GETVAL));
sleep(3);
}
}
共享内存
创建共享内存
int shmget(key_t key, size_t size, int shmflg);
共享内存的权限
SHM_R 读权限
SHM_W 写权限
设置共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
SHM_INFO
struct shm_info {
int used_ids; /* # of currently existing
segments */
unsigned long shm_tot; /* Total number of shared
memory pages */
unsigned long shm_rss; /* # of resident shared
memory pages */
unsigned long shm_swp; /* # of swapped shared
memory pages */
unsigned long swap_attempts;
/* Unused since Linux 2.4 */
unsigned long swap_successes;
/* Unused since Linux 2.4 */
};
SHM_STAT 共享内存状态
SHM_LOCK 锁定共享内存
SHM_UNLOCK 解锁共享内存
使用对象
void *shmat(int shmid, const void *shmaddr, int shmflg);
SHM_RDONLY
SHM_REMAP
SHM_EXEC
int shmdt(const void *shmaddr);
注意
1.父子进程的共享内存约定
fork函数
exec执行一个新的程序 自动卸载
exit
demo
共享内存和信号量结合的一个例子
recv
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "shmemory.h"
int main(int argc,char *argv[])
{
int running=1;
char *shm_p=NULL;
int shmid;
int semid;
int value;
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_flg = SEM_UNDO;
if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)
{
perror("semget");
exit(EXIT_FAILURE);
}
shmid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
if(shmid==-1)
{
perror("shmget");
exit(EXIT_FAILURE);
}
shm_p=shmat(shmid,NULL,0);
if(shm_p==NULL)
{
perror("shmat");
exit(EXIT_FAILURE);
}
while(running)
{
if((value=semctl( semid, 0, GETVAL ))==1)
{
printf("read data operate\n");
sem_b.sem_op = -1;
if (semop(semid, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
exit(EXIT_FAILURE);
}
printf("%s\n",shm_p);
}
if(strcmp(shm_p,"end")==0)
running--;
}
shmdt(shm_p);
if(shmctl(shmid,IPC_RMID,0)!=0)
{
perror("shmctl");
exit(EXIT_FAILURE);
}
if(semctl(semid,0,IPC_RMID,0)!=0)
{
perror("semctl");
exit(EXIT_FAILURE);
}
return 0;
}
send
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
int main(int argc,char *argv[])
{
int running=1;
int shid;
int semid;
int value;
void *sharem=NULL;
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_flg = SEM_UNDO;
if((semid=semget((key_t)123456,1,0666|IPC_CREAT))==-1)
{
perror("semget");
exit(EXIT_FAILURE);
}
if (semctl(semid, 0, SETVAL, 0) == -1)
{
printf("sem init error");
if(semctl(semid,0,IPC_RMID,0)!=0)
{
perror("semctl");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
shid=shmget((key_t)654321,(size_t)2048,0600|IPC_CREAT);
if(shid==-1)
{
perror("shmget");
exit(EXIT_FAILURE);
}
sharem=shmat(shid,NULL,0);
if(sharem==NULL)
{
perror("shmat");
exit(EXIT_FAILURE);
}
while(running)
{
if((value=semctl( semid, 0, GETVAL ))==0)
{
printf("write data operate\n");
printf("please input something:");
scanf("%s",sharem);
sem_b.sem_op = 1;
if (semop(semid, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_p failed\n");
exit(EXIT_FAILURE);
}
}
if(strcmp(sharem,"end")==0)
running--;
}
shmdt(sharem);
return 0;
}
域间套接字(socketpair)
demo
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void err_sys(const char *errmsg);
//父进程与子进程的通信
int main(void)
{
int sockfd[2];
pid_t pid;
if ((socketpair(AF_LOCAL, SOCK_STREAM,0, sockfd))<0)
err_sys("socketpair");
if ((pid = fork()) == -1)
err_sys("fork");
//子进程
else if (pid == 0)
{
// use sockfd[0]
char s[1000];
ssize_t n;
close(sockfd[1]); //write port
if ((n = read(sockfd[0], s, sizeof(s))) <0)
err_sys("read error!\n");
printf("read:%s\n",s);
close(sockfd[0]);
exit(0);
}
//父进程
else if (pid > 0)
{ /* parent process */
char buf[] = "hello china";
ssize_t n;
close(sockfd[0]); //read port
if((n = write(sockfd[1], buf, sizeof(buf)))<0)
err_sys("write error!\n");
close(sockfd[1]);
wait(NULL);
}
return 0;
}
void err_sys(const char *errmsg)
{
perror(errmsg);
exit(1);
}