我是靠谱客的博主 凶狠过客,这篇文章主要介绍Linux系统进程间通信编程,现在分享给大家,希望可以做个参考。

  进程控制原语,并且观察了如何调用多个进程。但是这些进程之间交换信息的唯一途径就是传送打开文件,经由fork或exec来传送,也可以通过文件系统来传送。本文将说明进程之间相互通信的其他技术——进程间通信(InterProcess Communication,IPC).IPC的方式通常有管道(无名管道和命名管道)、消息队列、共享内存、信号、信号量、Socket、stream(前五个为单机,后两个为多机).

   1.管道

    管道通常也称无名管道,是最古老的通信方式。

    特点:1.它是半双工的,具有固定的读端和写段。2.它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程)。3.它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

   fd[0]为读端,fd[1]为写端。

复制代码
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
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() { int pid; int fd[2]; char readBuf[1024] = {0}; // int pipe(int pipefd[2]); if(pipe(fd) == -1) { printf("create pipe failedn"); } pid = fork(); if( pid < 0 ) { printf("create fork failedn"); } else if(pid > 0) { sleep(3); printf("there is fathern"); close(fd[0]);//关闭读端,只能写 write(fd[1],"yi shu wei ban",strlen("yi shu wei ban")); wait(); } else if(pid == 0) { printf("there is childn"); close(fd[1]);//关闭写端,只能读 read(fd[0],readBuf,1024); printf("read from father : %sn",readBuf); exit(0); } return 0; }

   2.命名管道

      管道具有两种局限性:1.它们是半双工的。2.管道只能在具有公共祖先的两个进程之间使用。通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就只能在父子进程间使用。

       命名管道FIFO没有第二种局限性。

       创建管道并打开管道

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> int main() { // int mkfifo(const char *pathname, mode_t mode); if(mkfifo("./file1",0600) == -1 && errno != EEXIST) { printf("creat filedn"); perror("why"); } open("./file1",O_RDONLY); printf("open successn"); return 0; }

    当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。

  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> int main() { // int mkfifo(const char *pathname, mode_t mode); open("./file1",O_WRONLY); printf("write open successn"); return 0; }

 通过管道传递信息:

   read.c

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> int main() { int n_read = 0; int fd; // int mkfifo(const char *pathname, mode_t mode); char readBuf[30] = {0}; fd = open("./file1",O_RDONLY); while(1) { n_read = read(fd,readBuf,30); printf("read %d byte from fifo,context : %sn",n_read,readBuf); } close(fd); return 0; }

   write.c

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> int main() { int fd; char *str = "meesage from fifo"; // int mkfifo(const char *pathname, mode_t mode); fd = open("./file1",O_WRONLY); printf("write open successn"); while(1) { sleep(1); write(fd,str,strlen(str)); } close(fd); return 0; }

  3.消息队列

     消息队列:是消息的链接表,存放在内核。一个消息队列由一个标识符(即队列ID)来标识。

     特点:1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

                2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

                3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

   

复制代码
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
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf { long mtype;//类型 char mtext[128];//内容 }; int main() { struct msgbuf readbuf; struct msgbuf writebuf = {988,"thank you"}; int msqid; key_t key; key = ftok(".",1); printf("key = %xn",key); // int msgget(key_t key, int msgflg); if(msqid = msgget(key,IPC_CREAT|0777) == -1) { printf("failedn"); perror("why"); } // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); msgrcv(msqid,&readbuf,sizeof(readbuf.mtext),888,0); printf("read from que:%sn",readbuf.mtext); msgsnd(msqid,&writebuf,strlen(writebuf.mtext),0); // int msgctl(int msqid, int cmd, struct msqid_ds *buf); msgctl(msqid,IPC_RMID,NULL); 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
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf { long mtype; char mtext[128]; }; int main() { struct msgbuf writebuf = {888,"this is message from que"}; struct msgbuf readbuf; int msqid; key_t key; key = ftok(".",1); printf("key = %xn",key); // int msgget(key_t key, int msgflg); if(msqid = msgget(key,IPC_CREAT|0777) == -1) { printf("failedn"); perror("why"); } // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); msgsnd(msqid,&writebuf,strlen(writebuf.mtext),0); msgrcv(msqid,&readbuf,sizeof(readbuf.mtext),988,0); printf("return form get :%sn",readbuf.mtext); msgctl(msqid, IPC_RMID,NULL);//在内核中删除队列 return 0; }

  4.共享内存

     指两个或多个进程共享一个给定的存储区。(ipcs -m查看)

     特点:1.共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。

              2.因为多个进程可以同时操作,所以需要进行同步。

              3.信号量(同步)+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

     写

复制代码
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
#include <sys/ipc.h> #include <sys/types.h> #include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *shmaddr; int shmid; key_t key; key = ftok(".",1); // int shmget(key_t key, size_t size, int shmflg); shmid = shmget(key,1024*4,IPC_CREAT|0666);//钥匙介质,大小,方式 if(shmid == -1) { printf("failedn"); perror("why"); exit(-1); } exit(0); // void *shmat(int shmid, const void *shmaddr, int shmflg); shmaddr = shmat(shmid,0,0);//id,自动配置内存,模式(默认读写) printf("shmat okn"); strcpy(shmaddr,"yi shu wei ban"); sleep(5); // int shmdt(const void *shmaddr); shmdt(shmaddr); // int shmctl(int shmid, int cmd, struct shmid_ds *buf); shmctl(shmid,IPC_RMID,0); printf("quitn"); 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
#include <sys/ipc.h> #include <sys/types.h> #include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *shmaddr; int shmid; key_t key; key = ftok(".",1); // int shmget(key_t key, size_t size, int shmflg); shmid = shmget(key,1024*4,0);//获取不创建 if(shmid == -1) { printf("failedn"); perror("why"); exit(-1); } // void *shmat(int shmid, const void *shmaddr, int shmflg); shmaddr = shmat(shmid,0,0); printf("shmat okn"); printf("data is : %sn",shmaddr); sleep(5); // int shmdt(const void *shmaddr); shmdt(shmaddr); // int shmctl(int shmid, int cmd, struct shmid_ds *buf); printf("quitn"); return 0; }

  5.信号

     对于Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为Linux提供了处理异步事件的方法。比如,终端用户输入了ctrl+c来中断程序,会通过信号机制停止一个程序。

     每个信号都有一个名字和编号,可以使用(kill -l)查看信号的名字以及序号。信号的处理有三种方法,分别是:忽略、捕捉和默认动作。

  • 忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(SIGKILLSIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
  • 捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
  • 系统默认动作,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。

     信号注册函数:

复制代码
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
#include <stdio.h> #include <signal.h> // typedef void (*sighandler_t)(int); // sighandler_t signal(int signum, sighandler_t handler); void handler(int signum) { printf("get signum = %dn",signum); switch(signum) { case 2: printf("SIGINTn"); break; case 9: printf("SIGKILLn"); break; case 10: printf("SIGUSR1n"); break; } } int main() { signal(SIGINT,SIG_IGN); signal(SIGKILL,SIG_IGN); signal(SIGUSR1,handler);//信号捕捉 while(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
#include <stdio.h> #include <sys/types.h> #include <signal.h> int main(int argc,char **argv) { int pid; int signum; char cmd[128] = {0}; signum = atoi(argv[1]);//转换 pid = atoi(argv[2]);//转换 printf("num = %d,pid = %dn",signum,pid); // int kill(pid_t pid, int sig); kill(pid,signum);//kill指令发送信号 // sprintf(cmd,"kill -%d %dn",signum,pid); //构造字符串 // system(cmd); printf("kill okn"); return 0; }

   高级信号(在发送信号的同时还可以携带信息)

    信号注册函数:

 关于sigaction函数做下注释:1.信号号码或名字2.结构体3.备份

                          (1).结构体中(*sa_handler)(int)与简单信号的handler相似,不传递其他额外数据。

                          (2)信号处理函数,接受额外数据。括号里又包含3个类型:1.num2.结构体siginfo_t3.指针:有内容非空,无内容为空。

                          (3)阻塞信号集(默认阻塞)

                          (4)表示能够接受数据。                

 结构体siginfo_t:

  处理

复制代码
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
#include <stdio.h> #include <signal.h> // int sigaction(int signum, const struct sigaction *act, // struct sigaction *oldact); void handler(int signum,siginfo_t *info, void *context) { printf("get signum %dn",signum); if(context != NULL) { printf("get data = %dn",info->si_int); printf("get data = %dn",info->si_value.sival_int); printf("get pid = %dn",info->si_pid); } } int main() { struct sigaction act; printf("pid is %dn",getpid()); act.sa_sigaction = handler; act.sa_flags = SA_SIGINFO; sigaction(SIGUSR1,&act,NULL); while(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
#include <stdio.h> #include <signal.h> int main(int argc,char **argv) { union sigval value; int pid; int signum; signum = atoi(argv[1]); pid = atoi(argv[2]); value.sival_int = 100; // int sigqueue(pid_t pid, int sig, const union sigval value); sigqueue(pid,signum,value); printf("pid is %dn",getpid()); printf("donen"); return 0; }

   6.信号量

     信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

复制代码
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
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <unistd.h> // int semop(int semid, struct sembuf *sops, unsigned nsops); 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) */ }; // 若信号量值为1,获取资源并将信号量值-1 // 若信号量值为0,进程挂起等待 void PGetKey(int id)//P操作 { struct sembuf set; set.sem_num = 0; set.sem_op = -1; set.sem_flg = SEM_UNDO; semop(id,&set,1); //个数 } // 释放资源并将信号量值+1 // 如果有进程正在挂起等待,则唤醒它们 void VGet_back_Key(int id)//V操作 { struct sembuf set; set.sem_num = 0; set.sem_op = 1; set.sem_flg = SEM_UNDO; semop(id,&set,1); } int main() { int pid; int semid; union semun initsem; key_t key; key = ftok(".",2); // int semget(key_t key, int nsems, int semflg);//第二个参数:信号量集当中信号量的个数 semid = semget(key,1,IPC_CREAT|0666); initsem.val = 0; // int semctl(int semid, int semnum, int cmd, ...); //操作第0个信号量 semctl(semid,0,SETVAL,initsem);//初始化信号量 //setval设置信号量的值 pid = fork(); if(pid >0) { PGetKey(semid); printf("this is fathern"); VGet_back_Key(semid); semctl(semid,0, IPC_RMID); } else if(pid == 0) { printf("this is childn"); VGet_back_Key(semid); } else { printf("errorn"); } return 0; }

最后

以上就是凶狠过客最近收集整理的关于Linux系统进程间通信编程的全部内容,更多相关Linux系统进程间通信编程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(89)

评论列表共有 0 条评论

立即
投稿
返回
顶部