Linux进程通信

Linux 作为一个多任务多进程的操作系统,各个进程间信息交互
不可避免,进程间通信可分为本地进程间通信和远程进程间通信。本地进
程间通信主要包括信号,管道,消息队列,信号量,共享内存等通信方式。

管道通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*使用命名管道FIFO机制实现客户到服务器之间传递数据的操作。多客户-单一服务器模式*/
/*命名管道文件需创建在Linux文件系统内*/
/*在一个终端窗口中运行fifo-server程序,然后在另外一个终端窗口运行fifo-client程序*/

/*fifo-server.c */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>

#define FIFO_FILE "/tmp/MYFIFO" /*命名管道的路径、文件名*/

int main()
{
FILE *fp;
char readbuf[80];

if((fp=fopen(FIFO_FILE,"r"))==NULL)/*如果命名管道文件不存在,要先创建一个*/
{
umask(0);//清除文件创建时权限位的屏蔽作用
mknod(FIFO_FILE,S_IFIFO|0666,0);//创建FIFO文件
printf("create new fifo successed. \n");
}
else
fclose(fp);

while(1)
{
if((fp=fopen(FIFO_FILE,"r"))==NULL)/*打开命名管道文件*/
{
printf("open fifo failed. \n");
exit(1);
}

if(fgets(readbuf,80,fp)!=NULL)/*从命名管道文件中读数据*/
{
printf("Received string :%s \n", readbuf);
fclose(fp);
}
else
{
if(ferror(fp))
{
printf("read fifo failed.\n");
exit(1);
}
}
}
return 0;
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*使用命名管道FIFO机制实现客户到服务器之间传递数据的操作。多客户-单一服务器模式*/
/*命名管道文件需创建在Linux文件系统内*/
/*在一个终端窗口中运行fifo-server程序,然后在另外一个终端窗口运行fifo-client程序*/

/*fifo-client.c */

#include <stdio.h>
#include <stdlib.h>

#define FIFO_FILE "/tmp/MYFIFO"/*命名管道的路径、文件名*/

int main(int argc, char *argv[])
{
FILE *fp;
int i;
if(argc<=1)
{
printf("usage: %s <message>\n",argv[0]);
exit(1);
}

if((fp=fopen(FIFO_FILE,"w"))==NULL)/*打开命名管道文件*/
{
printf("open fifo failed. \n");
exit(1);
}

for(i=1;i<argc;i++)
{
if(fputs(argv[i],fp)==EOF)
{
printf("write fifo error. \n");
exit(1);
}
if(fputs(" ",fp)==EOF)
{
printf("write fifo error. \n");
exit(1);
}
}
fclose(fp);
return 0;

}

消息队列

发送端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*使用消息队列机制实现发送接收消息的操作。*/
/*在一个终端窗口中运行msg-send程序,然后在另外一个终端窗口运行msg-recieve程序*/

/*msg-send.c */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct my_msg//消息的组成
{
long int my_msg_type;//消息的类型域
char text[BUFSIZ];//消息传递的数据域
} msgbuf;

int main()
{
int running =1;
int msgid;
msgid=msgget((key_t)1234,0666 |IPC_CREAT);//打开key值为1234的消息队列,如不存在则创建之
if(msgid==-1)
{
printf("msgget failed!\n");
exit(1);
}
while(running)
{
printf("Enter some text: ");
fgets(msgbuf.text,BUFSIZ,stdin);//读入键盘输入的消息
msgbuf.my_msg_type=1;
if(msgsnd(msgid,(void *)&msgbuf, BUFSIZ, 0)==-1)//发送消息
{
printf("msgsnd failed!\n");
exit(1);
}
if(strncmp(msgbuf.text,"end",3)==0)//输入end表示程序结束
running=0;
}

return 0;
}

接收端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*使用消息队列机制实现发送接收消息的操作。*/
/*在一个终端窗口中运行msg-send程序,然后在另外一个终端窗口运行msg-recieve程序*/

/*msg-recieve.c */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct my_msg//消息的组成
{
long int my_msg_type;//消息的类型域
char text[BUFSIZ];//消息传递的数据域
} msgbuf;

int main()
{
int running =1;
int msgid;
long int msg_to_receive=0;
msgid=msgget((key_t)1234,0666 |IPC_CREAT);//打开key值为1234的消息队列,如不存在则创建之
if(msgid==-1)
{
printf("msgget failed!\n");
exit(1);
}
while(running)
{
if(msgrcv(msgid,(void *)&msgbuf, BUFSIZ,msg_to_receive, 0)==-1)//接收消息
{
printf("msgrcv failed!\n");
exit(1);
}
printf("You wrote : %s", msgbuf.text);
if(strncmp(msgbuf.text,"end",3)==0)//收到end表示程序结束
running=0;
}
if(msgctl(msgid, IPC_RMID, 0)==-1)//删除消息队列
{
printf("msgct(IPC_RMID) failed!\n");
exit(1);
}
return 0;
}

信号量机制

写入端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*使用共享内存机制实现通信*/
/*在一个终端窗口中先运行shm-write程序,然后在另外一个终端窗口运行shm-read程序*/

/*shm-write.c */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
int shmid;
char c;
char *shmptr, *s;
if((shmid=shmget(1234,256,IPC_CREAT | 0666))<0)//打开key值为1234的共享内存,如不存在则创建之
{
printf("shmget failed.\n");
exit(1);
}
if((shmptr=shmat(shmid,0,0))==(char*)-1)//附加此共享内存至自己的地址空间,返回内存区域的指针
{
shmctl(shmid, IPC_RMID, (struct shmid_ds*)shmptr);
printf("shmat failed.\n");
exit(2);
}
s=shmptr;// 写共享内存通过指针s操作
for(c='a';c<='z';c++)//写入26个字母
*s++=c;
s='\0';
while(*shmptr!='*')//等待直到读进程已写入“*”表示读完数据
sleep(1);
shmctl(shmid, IPC_RMID, (struct shmid_ds*)shmptr);//删除共享内存
return 0;
}

读取端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*使用共享内存机制实现通信*/
/*在一个终端窗口中先运行shm-write程序,然后在另外一个终端窗口运行shm-read程序*/

/*shm-read.c */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{

int shmid;
char c;
char *shmptr, *s;
if((shmid=shmget(1234,256, 0666))<0)//打开key值为1234的共享内存
{
printf("shmget failed.\n");
exit(1);
}
if((shmptr=shmat(shmid,0,0))==(char*)-1)//附加此共享内存至自己的地址空间
{
shmctl(shmid,IPC_RMID,(struct shmid_ds*)shmptr);
printf("shmat failed.\n");
exit(2);
}
for(s=shmptr;*s!='\0';s++)//读出26个字母
putchar(*s);
printf("\n");
*shmptr='*';//写入“*”到共享内存表示读完数据
return 0;
}

共享内存

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*共享内存机制可以直接读取内存,所以其通信效率高于管道和消息队列*/
/*由于多个进程对同一块内存区域具有访问的权限,进程之间的同步就非常重要*/
/*使用信号量机制PV操作来同步的共享内存机制通信*/
/*在一个终端窗口中先运行semshm-write程序,然后在另外一个终端窗口运行semshm-read程序*/

/*semshm-.h */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <error.h>

#define SHM_SIZE 1024

union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *buf_info;
void *pad;
};

/* 创建信号量函数*/
int creatsem(const char *pathname, int proj_id, int members, int init_val)
{
key_t msgkey;
int index, sid;
union semun semopts;

if((msgkey = ftok(pathname, proj_id)) == -1){//利用ftok函数生成键值,自行指定的键值可能会冲突
perror("ftok error!\n");
return -1;
}
if((sid = semget(msgkey, members, IPC_CREAT|0666)) == -1){//打开键值为msgkey的信号量集,如不存在则创建之,返回信号量集标识符。members为信号量集中含信号量的数目。
perror("semget call failed.\n");
return -1;
}
semopts.val = init_val;
for(index = 0; index < members; index++){
semctl(sid, index, SETVAL, semopts);
}

return sid;
}

int opensem(const char *pathname, int proj_id)
{
key_t msgkey;
int sid;

if((msgkey = ftok(pathname, proj_id)) == -1){
perror("ftok error!\n");
return -1;
}

if((sid = semget(msgkey, 0, 0666)) == -1){
perror("open semget call failed.\n");
return -1;
}
return sid;
}

/* p操作, 获取信号量*/
int sem_p(int semid, int index)
{
struct sembuf sbuf = {0, -1, IPC_NOWAIT};//每个sembuf结构描述了一个对信号量的操作
if(index < 0){
perror("index of array cannot equals a minus value!\n");
return -1;
}
sbuf.sem_num = index;
if(semop(semid, &sbuf, 1) == -1){
perror("A wrong operation to semaphore occurred!\n");
return -1;
}
return 0;
}

/* V操作, 释放信号量*/
int sem_v(int semid, int index)
{
struct sembuf sbuf = {0, 1, IPC_NOWAIT};//每个sembuf结构描述了一个对信号量的操作
if(index < 0){
perror("index of array cannot equals a minus value!\n");
return -1;
}
sbuf.sem_num = index;
if(semop(semid, &sbuf, 1) == -1){
perror("A wrong operation to semaphore occurred!\n");
return -1;
}
return 0;
}

/* 删除信号量*/
int sem_delete(int semid)
{
return (semctl(semid, 0, IPC_RMID));
}

/* 等待信号量*/
int wait_sem(int semid, int index)
{
while(semctl(semid, index, GETVAL, 0) == 0)
{
usleep(500);
}
return 1;

}

/* 创建共享内存*/
int creatshm(char *pathname, int proj_id, size_t size)
{
key_t shmkey;
int sid;

if((shmkey = ftok(pathname, proj_id)) == -1){
perror("ftok error!\n");
return -1;
}
if((sid = shmget(shmkey, size, IPC_CREAT|0666)) == -1){
perror("shm call failed!\n");
return -1;
}
return sid;
}

/* 删除共享内存*/
int deleteshm(int sid)
{
void *p = NULL;
return (shmctl(sid, IPC_RMID, p));
}

写入端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*共享内存机制可以直接读取内存,所以其通信效率高于管道和消息队列*/
/*由于多个进程对同一块内存区域具有访问的权限,进程之间的同步就非常重要*/
/*使用信号量机制PV操作来同步的共享内存机制通信*/
/*在一个终端窗口中先运行semshm-write程序,然后在另外一个终端窗口运行semshm-read程序*/

/*semshm-write.c */

#include "2-4semshm-.h"

int main(int argc, char** argv)
{
int semid, shmid;
char *shmaddr;
char write_str[SHM_SIZE];
char *ret;
if((shmid = creatshm(".", 57, SHM_SIZE)) == -1) //创建或者获取共享内存
return -1;
/*建立进程和共享内存连接*/
if((shmaddr = shmat(shmid, (char*)0, 0)) == (char *)-1){
perror("attch shared memory error!\n");
exit(1);
}
if((semid = creatsem("./", 39, 1, 1)) == -1)//创建信号量
return -1;
while(1){
wait_sem(semid, 0);//等待信号量可以被获取
sem_p(semid, 0); //获取信号量
/***************写共享内存***************************************************/
printf("write : ");
ret = fgets(write_str, 1024, stdin);
if(write_str[0] == '#') // '#'结束读写进程
break;
int len = strlen(write_str);
write_str[len] = '\0';
strcpy(shmaddr, write_str);
/****************************************************************************/
sem_v(semid, 0); //释放信号量
usleep(1000); //本进程睡眠.
}
sem_delete(semid); //把semid指定的信号集从系统中删除
deleteshm(shmid); //从系统中删除shmid标识的共享内存
return 0;
}

读取端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*共享内存机制可以直接读取内存,所以其通信效率高于管道和消息队列*/
/*由于多个进程对同一块内存区域具有访问的权限,进程之间的同步就非常重要*/
/*使用信号量机制PV操作来同步的共享内存机制通信*/
/*在一个终端窗口中先运行semshm-write程序,然后在另外一个终端窗口运行semshm-read程序*/

/*semshm-read.c */

#include "2-4semshm-.h"

int main(int argc, char** argv)
{
int semid, shmid;
char *shmaddr;
if((shmid = creatshm(".", 57, SHM_SIZE)) == -1)
return -1;
if((shmaddr = shmat(shmid, (char*)0, 0)) == (char *)-1){
perror("attch shared memory error!\n");
exit(1);
}
if((semid = opensem("./", 39)) == -1)
return -1;
printf("read start....................\n");
while(1){
printf("read : ");
wait_sem(semid, 0); //等待信号量可以获取
if(sem_p(semid, 0) == -1) //获取信号量失败退出。当server写入'#'时引发
break;
printf("%s", shmaddr);

sem_v(semid, 0);
usleep(1000);
}
return 0;
}