进程间通信IPC:Inter-process communication(这边介绍四种)


进程间通信IPC:Inter-process communication(这边介绍四种)
 
一、管道
1、管道(有亲缘关系)及有名管道(无亲缘)、信号、消息队列、共享内存、信号量、套接字。
=====无名管道
2、管道:是堵塞的;管道的读端存在时向管道中写入数据才有意义。具有固定的读端和写端。
3、当一个管道建立pipe(fds)时,它会创建两个文件描述符 fds[0]和 fds[1]。结束后要关闭,close(fds[0]);close(fds[1]);
4、一般是先建一个管道,再通过fork(),则会有两个管道。其中 fds[0]固定用于读管道,而 fd[1]固定用于写管道具有父子亲缘关系。为了实现父子进程之间的读写,只需把无关的读端或写端的文件描述符关闭即可。一边读,一边写。
  www.2cto.com  
5、最后也要等待,等子进程结束后父进程再结束。防止僵尸进程,没人收。
6、注意:1.只有在管道的读端存在时向管道中写入数据才有意义。2.向管道中写入数据时,linux 将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。3.父子进程在运行时,它们的先后次序并不能保证,因此,在这里为了保证父进程已经关闭了读描述符,可在子进程中调用 sleep 函数。
=======标准流管道
1、标准流管道:用来创建一个连接到另一个进程的管道。所以不属于有名或无名管道。
2、popen,"r"只是把结果作为输出,但还没有输出。返回的是管道的文件描述符。"w"貌似不可用-lai。==对应 pclose(fd);
=======有名管道(相当于把创建的文件当做管道连接,然后像对普通文件一样进行读写。文件也就是管道可以通过路径名来指出,而且在文件系统中是可见的)
1、有名管道:互不相关的两个进程实现彼此通信。(mkfifo)管道可以通过路径名来指出,也可以mknod  管道名  p  来创建
2、普通文件读写不会出现堵塞问题,管道读写中却有堵塞的可能。非堵塞标志:O_NONBLOCK.
3、如果文件已经存在,则也可以成功,但是会出现“交叉读写”现象,所以尽量避免。
4、但是以上测试还是有点问题,最好用完后就删掉,不然第二次编译不了===赖
5、#define FIFO_SERVER "/linux_basic_study/pro_communication/myfifo"
6、//只要创建一次,文件就会存在,以后直接打开也可以。
//可以看到以P开头的管道文件在/linux_basic_study/myfifo
//errno是系统定义的全局变量,函数发生异常时,一般会将errno变量(需include  errno.h)赋一个整数值,  www.2cto.com  
//不同的值表示不同的含义,可以通过查看该值推测出错的原因.
7.//阻塞读时要每读一次就open一次阻塞仅在第一次读有效。否则第一次堵塞后,后面就不堵塞了。//非阻塞时open要放while外面,不然接收不到数据.
8.unlink(FIFO);//删除文件。记住:要保证里面没有文件。对应第四条。
======================================================================
二、信号通信
1.信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。信号可以直接进行用户空间进程和内核进程之间的交互。相当于我们的中断处理函数
2.信号生命周期3个重要阶段==>通过四个画面:信号产生(内核)、信号在进程中注册(用户进程)、信号在进程中注销(用户进程)、执行信号处理函数。
3.不可靠信号(前)32个,若发现同样的该信号已经注册,则忽略。可靠信号则会再次注册。
4.注意:信号的产生、注册、注销等是指信号的内部实现机制,而不是信号的函数实现。
5.三种=>忽略信号(SIGKILL及SIGSTOP不能被忽略):对信号不做处理;捕捉信号:当信号发生时,执行相应的处理函数;执行缺省操作:Linux 对每种信号都规定了默认操作。
6.重要的六个信号:SIGINT=>(CTRL+C)f发出;  SIGQUIT=>(CTRL+\) SIGKILL SIGALARM SIGSTOP SIGCHLD=>(CTRL+Z)
7.Alarm()一个进程只能有一个闹钟时间。时间到后,发出SIGALRM信号,自动终止 程序执行 。
8.Pause()用于将调用进程挂起直至捕捉到信号为止。
9.Raise()类似于kill函数允许进程而且只能向自身发送信号。
10.Kill()不仅可以中止进程(实际上发出 SIGKILL 信号),也可以向进程发送其他 信号。
11.信号处理的主要方法有两种,一种是使用简单的 signal 函数 eg:signal(SIGINT, my_func);,另一种是 使用信号集函数组。
12.父进程为1是孤儿进程,是脱离控制台。Kill不掉。SIGSTOP用产生。。 只能重启系统才能关掉孤儿进程。所以最好发送KILL信号,不要发送stop
三、共享内存(创建一个共享内存,可以映射到自己的内存上,通过同步机制。)
利用共享内存存在问题:如果在循环里面,它会循环读取已经读过的内容。
可同时利用后面讲到的消息队列解决这个问题。
1、共享内存是一种最为高效的进程间通信方式。===>非阻塞。。。删除一次就可以了,删除两次会出错。
2、是将需要访问的内存映射到自己私有的地址空间。所以相当于访问自己的内存。
 
3、步骤:1,创建共享内存:最好ipc_creat(没有e)...不然如果这个key 的内存没有 创建的话会找不到文件会报错。返回的是共享内存 区标识符
shmid = shmget((key_t)100,BUFSZ,0666|IPC_CREAT);
  2、映射到共享内存。返回的是共享内存映射到指定位置的指针
shmadd = shmat(shmid ,0 ,0);
3、读取或者写入
if(!strncmp(shm_ptr,"end",3);
if(!strcmp(shm_ptr, "end"))//不行,不要用,第一次可以,后面就 不行
4、撤销映射内存shmdt(shmadd)
5、删除共享内存:记住:读或者写一方删除就可以了,不然会 报错。
6、在输入时,用fgets(temp, 1024, stdin); 键盘输入,标准stdin;
用scanf("%s",temp);//中间不能有空格,因为是以空格或者回车 为结束。所以不采用。
四、消息队列
1、消息队列具有一定的FIFO 的特性,但是它可以实现消息的随机 查询,比FIFO 具有更大的优势。
2、也是一个放送,一个接收。两种方法:一个用结构体,一个不要(其实项目中要的)。
3、步骤:
1、创建或打开消息队列:返回的是消息队列ID
msg_id = msgget((key_t)1234, 0666 | IPC_CREAT);
2、读取消息:
msgrcv(msg_id,buf, BUFSIZ, 0, 0)
2、发送添加消息:
msgsnd(message_id,buf,BUFSZ,0)
3、控制消息队列:相当于撤销,删除。
msgctl(message_id, IPC_RMID, 0) 
4、利用结构体,发送数据,但发送过去的只是字符串,而 message_type只是作为接收第几个信息的标志。所以一 般用另一种。
 

相关内容

    暂无相关文章