进程间通信

本文详细介绍了Linux系统中的进程间通信(IPC)机制,包括管道、有名管道(FIFO)、消息队列、信号量和共享内存。管道在父子进程间通信,FIFO可在无关进程间交换数据。消息队列提供了有序的消息传递并可按类型读取。信号量用于进程同步,而非数据传递。共享内存允许快速的进程间数据共享,但需注意同步问题。这些机制在多进程协同工作时起到关键作用。
摘要由CSDN通过智能技术生成

我啥也不是奥,就是做个笔记要真看去看这几个大哥的:
https://blog.csdn.net/xiaonan153/article/details/81502245

https://www.cnblogs.com/zgq0/p/8780893.html

又发现个大哥这个全,太全了笔记都懒得记了:

[linux基础——linux进程间通信(IPC)机制总结]

linux基础——linux线程间通信及同步机制总结

管道

创建进程之前创建管道,

原型 :

#include <unistd.h>
int pipe(int fd[2]);

建立线程之后要关闭自己不用的那个:

close(fd[0]);

read和write 读写
fd[0]是读段
fd[1]是写段

FIFO

又名命名管道,是一种文件类型

特点
1.有名管道可以在无关进程间交换数据,这点无名管道做不到
2.有名管道有储存路径,他以一种特殊文件储存在系统文件中

原型

#include <sys/stat.h>

int mkfifo(const char * pathname, mode_t mode);

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

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

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

消息队列

消息队列,是消息的连接表, 存放在内核中,一个消息队列由一个标识符(id)来标记
特点
1.消息队列是面向记录的,其中的消息具有特点的格式的优先级
2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按照类型读取

原型

#include <sys/msg.h>
//创建或者打开消息队列,成功返回队列id,失败返回-1
int msgget(key_t key, int flag);
 // 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
 // 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
 // 控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

以下两种情况,msgget将创建一个新的消息队列:

如果没有key值对应的消息队列,并且flag中包含了IPC_CREAT标志位
key值参数为IPC_PRIVATE

函数msgrcv在读取消息1队列时,type参数有下面几种情况:
type == 0,返回队列中的第一个消息;
type > 0,返回队列中消息类型为 type 的第一个消息;
type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。

信号量

是一个计数器,用来控制进程间的互斥与同步,不是用来存储进程间的通信数据。

特点
1.信号量用于进程间同步,若在 进程间传递数据还需要共享内存
2.信号量基于操作系统的pv操作,程序多信号量的操作都是原子操作。
3.每次1对信号量的pv操作不仅限于对信号量值的加1或者减1,可以加减任意数。
4.支持信号量组

PV操作:一种实现进程互斥与同步的有效方法,包含P操作与V操作。

P操作:使 S=S-1 ,若 S>=0 ,则该进程继续执行,否则排入等待队列。

V操作:使 S=S+1 ,若 S>0 ,唤醒等待队列中的一个进程。

原子操作:原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始就直接运行结束,中间不会有任何切换操作。

原型

 #include <sys/sem.h>
 // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
 int semget(key_t key, int num_sems, int sem_flags);
 // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
 int semop(int semid, struct sembuf semoparray[], size_t numops);  
 // 控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);

当创建一个新的信号量集合是num_sems是集合中的信号量个数,当引用一个现有的集合,将num_senms指定为0;

例子
 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/sem.h>
  4 
  5 // 联合体,用于semctl初始化
  6 union semun
  7 {
  8     int              val; /*for SETVAL*/
  9     struct semid_ds *buf;
 10     unsigned short  *array;
 11 };
 12 
 13 // 初始化信号量
 14 int init_sem(int sem_id, int value)
 15 {
 16     union semun tmp;
 17     tmp.val = value;
 18     if(semctl(sem_id, 0, SETVAL, tmp) == -1)
 19     {
 20         perror("Init Semaphore Error");
 21         return -1;
 22     }
 23     return 0;
 24 }
 25 
 26 // P操作:
 27 //    若信号量值为1,获取资源并将信号量值-1 
 28 //    若信号量值为0,进程挂起等待
 29 int sem_p(int sem_id)
 30 {
 31     struct sembuf sbuf;
 32     sbuf.sem_num = 0; /*序号*/
 33     sbuf.sem_op = -1; /*P操作*/
 34     sbuf.sem_flg = SEM_UNDO;
 35 
 36     if(semop(sem_id, &sbuf, 1) == -1)
 37     {
 38         perror("P operation Error");
 39         return -1;
 40     }
 41     return 0;
 42 }
 43 
 44 // V操作:
 45 //    释放资源并将信号量值+1
 46 //    如果有进程正在挂起等待,则唤醒它们
 47 int sem_v(int sem_id)
 48 {
 49     struct sembuf sbuf;
 50     sbuf.sem_num = 0; /*序号*/
 51     sbuf.sem_op = 1;  /*V操作*/
 52     sbuf.sem_flg = SEM_UNDO;
 53 
 54     if(semop(sem_id, &sbuf, 1) == -1)
 55     {
 56         perror("V operation Error");
 57         return -1;
 58     }
 59     return 0;
 60 }
 61 
 62 // 删除信号量集
 63 int del_sem(int sem_id)
 64 {
 65     union semun tmp;
 66     if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
 67     {
 68         perror("Delete Semaphore Error");
 69         return -1;
 70     }
 71     return 0;
 72 }
 73 
 74 
 75 int main()
 76 {
 77     int sem_id;  // 信号量集ID
 78     key_t key;  
 79     pid_t pid;
 80 
 81     // 获取key值
 82     if((key = ftok(".", 'z')) < 0)
 83     {
 84         perror("ftok error");
 85         exit(1);
 86     }
 87 
 88     // 创建信号量集,其中只有一个信号量
 89     if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
 90     {
 91         perror("semget error");
 92         exit(1);
 93     }
 94 
 95     // 初始化:初值设为0资源被占用
 96     init_sem(sem_id, 0);
 97 
 98     if((pid = fork()) == -1)
 99         perror("Fork Error");
100     else if(pid == 0) /*子进程*/ 
101     {
102         sleep(2);
103         printf("Process child: pid=%d\n", getpid());
104         sem_v(sem_id);  /*释放资源*/
105     }
106     else  /*父进程*/
107     {
108         sem_p(sem_id);   /*等待资源*/
109         printf("Process father: pid=%d\n", getpid());
110         sem_v(sem_id);   /*释放资源*/
111         del_sem(sem_id); /*删除信号量集*/
112     }
113     return 0;
114 }

没仔细看就copy个例子以后看

共享内存

共享内存,指两个或多个进程共享一个给定的储存区

特点

1.共享内存是一种最快的ipc(进程间通讯)因为进程是直接对进程进行存取的。
2.因为多个进程可以同时操作所以需要进行同步。
3.信号量加共享内存经常结合在一起使用,信号量用来同步对共享内存的访问。

2.原型

1 #include <sys/shm.h>
2 // 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
3 int shmget(key_t key, size_t size, int flag);
4 // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
5 void *shmat(int shm_id, const void *addr, int flag);
6 // 断开与共享内存的连接:成功返回0,失败返回-1
7 int shmdt(void *addr); 
8 // 控制共享内存的相关信息:成功返回0,失败返回-1
9 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。

当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。

shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。

shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。

信号

一、简述

  记--进程之间使用信号进行通信。异步信号包含: 非实时信号 、实时信号。

   实时信号一定会响应,非实时信息号不一定会响应(可能会被忽略,或丢失)

 信号一般有以下设定:
        1,捕捉    (收到某个信号,做指定的动作,而不是做默认的)
        2,忽略    (收到某个信号,不做什么动作)
        3,阻塞    (收到某个信号,先做完当前事情,然后在响应信号)
        4,按照默认动作

 注:SIGKILL,SIGSTOP不能被捕捉

二 。 信号函数signal()与kill()

signal()函数
功能 简单的信号处理函数(信号捕捉函数)
头文件 #include <signal.h>
原型
typedef void (*sighandler_t)(int); //信号处理函数的 指针

sighandler_t signal(int signum, sighandler_t handler);

参数
signum:要捕捉的信号

handler:处理方式
1、SIG_IGN:忽略这个信号
2、SIG_DFL:按照默认动作执行这个信号
3、如果是一个函数,则是捕捉这个信号,收到这个信号的时候去执行这个函数。
返回值
成功:返回信号处理程序的先前值
出错:返回SIG_ERR,并设置errno

kill()函数
功能 向一个进程发送信号
头文件 #include <sys/types.h>
#include <signal.h>
原型 int kill(pid_t pid, int sig);
参数
pid:进程ID (process id)
sig:要发送的信号
返回值
成功:返回0
失败:返回-1,并设置error。

套接字

流式套接字:提供可靠的,面向连接的通讯流

数据包套接字:定义一种无连接的服务,通过相互独立的报文进行传输,是无序的

原始套接字:用于新的网络协议的测试

总结

1.管道。速度慢容量有限,只能父子进程通讯
2。FIFO:任何进程间都能通讯,但是速度慢
3.消息队列 容量收到系统限制,且要注意第一次读的时候要考虑上一次没读完的数据
4.信号量,不能传递复杂消息只能用来同步。
5.共享内存,能控制容量速度快,但是要保持同步,共享内存可以进行线程间通信但是没必要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值