概念 关键 进程通信:在用户空间实现进程通信是不可能的,通过Linux内核通信 线程间通信:可以在用户空间 就可以实现,可以通过全局变量 通信。
方式 管道通信 :无名管道、有名管道(文件系统中有名,文件系统)信号通信 :信号(通知)通信包括:信号的发送、信号的接收和信号的处理。IPC(Inter-Process Communication)通信 :共享内存、消息队列和信号灯。 以上是单机模式 下的进程通信(只有一个L加ux内核)Socket通信:存在于一个网络中两个进程之间的通信(两个Linux内核)。
通信实现的思想
基于文件IO理念
open:功能:创建或打开进程通信对象。函数形式不一样,有的是有多个函数完成。 write:功能:向进程通信对象中写入内容。函数形式可能不一样。 read:功能:从进程通信对象中读取内容。函数形式可能不一样。 close:功能:关闭或删除进程通信对象。形式可能不一样。
管道通信 原理
管道文件是一个特殊的文件,是由队列来实现的。
无名管道 1 2 #include <unistd.h> int pipe (int pipefd[2 ]) ;
水从高处流 The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.
管道是创建在内存中的,进程结束,空间释放,管道就不存在了;
管道中的东西,读完了就删除了;队列
如果管道中没有东西可读,则会阻塞
无名管道的创建 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 #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main () { int fd[2 ]; int ret; ret = pipe(fd); if (ret < 0 ) { printf ("create pipe failure" ); return -1 ; } printf ("create pip sucess fd[0]=%d,fd[1]=%d\n" ,fd[0 ],fd[1 ]); return 0 ; }
Linux内核在创建进程的时候会默认创建三个文件描述符,即0,1,2。所以无名管道返回的文件描述符为3,1、4
无名管道的读写 关键
管道是创建在内存中的,进程结束,空间释放,管道就不存在了;
管道中的东西,读完了就别除了:队列
如果管道中没有东西可读,则会阻塞。
验证写阻塞:可以计算出内核开辟的管道有多大,队列超过这么大会发生写堵塞 。5456 5457.
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 #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main () { int fd[2 ]; int ret; char writebuff[]="hello Linux" ; char readbuff[128 ]={0 }; ret = pipe(fd); if (ret < 0 ) { printf ("create pipe failure" ); return -1 ; } printf ("create pip sucess fd[0]=%d,fd[1]=%d\n" ,fd[0 ],fd[1 ]); write(fd[1 ],writebuff,sizeof (writebuff)); read(fd[0 ],readbuff,128 ); printf ("first readbuff = %s\n" ,readbuff); read(fd[0 ],readbuff,128 ); printf ("second readbuff = %s\n" ,readbuff); close(fd[0 ]); close(fd[1 ]); return 0 ; }
无名管道的缺点 :不能实现不是父子进程(亲缘关系)之间的通信。
有名管道
由于无名管道的缺点,对无名管道进行改进:有名管道 所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个node号 而且这是一个特殊的文件类型:p管道类型 。
Create named pipes (FIFOs) with the given NAMEs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> int main () { int ret; ret = mkfifo("./myfifo" ,0777 ); if (ret<0 ) { printf ("create pip failure by file. ret = %d/n" , ret); return -1 ; } printf ("create sucess\n" ); return 0 ; }
创建这个文件节点,不可以通过open函数,open函数只能创建普通文件,不能创建特殊文件(管道-mkdifo,套接字-socket,字符设备文件-mknod,块设备文件-mknod, 符号链接文件-ln-s,目录文件mkdir)
管道文件只有node号,不占磁盘块空间 ,和套接字、字符设备文件、块设备文件一 样。普通文件和符号链接文件及目录文件,不仅有node号,还占磁盘块空间。
mkfifo用来创建管道文件的节点 ,没有在内核中创建管道 。只有通过open函数打开这个文件时才会在内核空间创建管道。
利用有名管道进行进程通信
基于上一步创建的管道节点进行通信,进程一等待五秒后向通道发送信息。
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 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> int main () { int fd; char process_state=0 ; int i=0 ; fd = open("./myfifo" ,O_WRONLY); if (fd<0 ) { printf ("open pipe node failure\n" ); return -1 ; } printf ("open pipe node sucess\n" ); for (i=0 ; i<5 ;i++) { printf (" this is first process i=%d\n" ,i); usleep(100 ); } process_state = 1 ; sleep(5 ); write(fd,&process_state,1 ); 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 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> int main () { int fd; char process_state=0 ; int i=0 ; fd = open("./myfifo" ,O_RDONLY); if (fd<0 ) { printf ("open pipe node failure\n" ); return -1 ; } printf ("open pipe node sucess\n" ); read(fd,&process_state,1 ); while (process_state==0 ); for (i=0 ; i<5 ;i++) { printf (" this is second process i=%d\n" ,i); usleep(100 ); } while (1 ); return 0 ; }
信号通信 信号发送
信号通信的框架
信号的发送(发送信号进程):kill raise alarm
信号的接收(接收信号进程):pause() sleep while(1)
信号的处理(接收信号进程):signal
kill函数的使用
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 #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <signal.h> #include <stdlib.h> int main (int argc,char *argv[]) { int sig; int pid; if (argc<3 ) { printf ("please input param\n" ); return -1 ; } sig = atoi(argv[1 ]); pid = atoi(argv[2 ]); printf ("sig=%d, pid = %d\n" ,sig,pid); kill(pid,sig); return 0 ; }
简单使用kill,waitpid
案例: 开始时主进程处于睡眠状态S,子进程处于暂停T. 八秒后主进程杀死子进程并利用while进入S. 但是子进程被杀死后没有回收资源所以进入僵尸状态Z.
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 #include <stdio.h> #include <sys/types.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main () { pid_t pid; pid = fork(); if (pid > 0 ) { sleep(8 ); if (waitpid(pid,NULL ,WNOHANG) ==0 ) { kill(pid,9 ); } while (1 ); } if (pid == 0 ) { printf ("raise function before\n" ); raise(SIGTSTP); printf ("raise function after\n" ); exit (0 ); } return 0 ; }
信号处理
signal函数:返回一个函数指针. 含有两个参数,第一个为信号值, 第二个为一个函数指针,其中返回值为void.默认处理信号方式为终止信号.
alarm处理信号 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 #include <stdio.h> #include <sys/types.h> #include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> void myfun (int signum) { int i; i=0 ; while (i<10 ) { printf ("process sleep %d s!\n" ,i); sleep(1 ); i++; } return ; } int main () { int i =0 ; signal(14 ,myfun); printf ("alarm start\n" ); alarm(9 ); printf ("next step\n" ); while (i<20 ) { i++; sleep(1 ); printf ("main do %d s\n" ,i); } return 0 ; };
signal信号处理
父子进程进行通信
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 #include <stdio.h> #include <sys/types.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> void myfun (int signum) { int i=0 ; while (i<5 ) { printf ("receive signum = %d, i = %d\n" ,signum,i); sleep(1 ); i++; } } void myfun1int main () { pid_t pid; pid = fork(); printf ("PID is %d\n" ,pid); if (pid > 0 ) { int i = 0 ; signal(10 ,myfun); while (1 ) { printf ("A say %d\n" ,i); sleep(1 ); i++; } } if (pid == 0 ) { sleep(10 ); kill(getppid(),10 ); sleep(3 ); exit (0 ); } return 0 ; }