引言:IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
一、管道
概念:管道分为无名管道和命名管道。管道是一种半双工的通信方式(半双工即信息既可由A传到B,又能由B传A,但只能由一个方向上的传输存在),数据只能单向流动。管道的问题在于他们没有名字,只能在具有亲缘关系(父子进程间)的进程间使用。
管道特点:
①半双工的通信方式。
②只能用于具有亲缘关系的进程间的通信。
③管道不储存数据,只存在于内存。
1、无名管道
函数原型:
#include
int pipe(int fd[2]);
返回值为整形,若成功则范围0,失败则返回1.
其中fd[0]为只读打开,fd[1]为只写打开.
2、命名管道
#include
#include
int mkfifo(const char *pathname, mode_t mode);
返回值为整形,若成功则范围0,失败则返回1.
参数const char *pathname:路径名
mode_t mode:一般使用0600(可读可写)
注意:命名管道一般与open、read、write合用进行传送消息.
二、消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点:
①消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
②消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
③消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
#include
1.创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
key相当于为索引(可用ftok函数获取key值),通过key在linux找到相应的队列。
2.添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
3.读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
4.控制消息队列(队列移除):成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
eg:msgctl(msgID,IPC_RMID,NULL);
三、共享内存
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区
特点:
①共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
②多个进程可以同时操作,需要进行同步。
③信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
#include
1. 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
2 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
eg:shamat(shmID,0,0) //让内核自动安排共享内存,并具备可读可写.
3 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr);
4 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
eg:shmctl(shmID,IPC_RMID,NULL);
实现共享内存编程思路:
①创建共享内存 shmget()
②映射 shmat()
③进行数据交换(可直接对地址进行赋值strcpy)。
④释放共享内存 shmdt()
⑤关闭 shmctl()
四、信号
1、信号的处理:
信号的处理有三种方法,分别是:忽略、捕捉和默认动作
忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景
捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
系统默认动作,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。
具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。在此,我就不详细展开了,需要查看的,可以自行查看。
2、信号的使用:
kill -信号编号 进程id号
3、信号处理函数
①入门版signal
发送指令kill
int kill(pid_t pid, int sig);
绑定信号signal
sighandler_t signal(int signum, sighandler_t handler);
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
第一个参数为:捕捉的信号
第二个参数为:信号处理函数
void handler(int signum){
}
忽略信号(依然可以杀死)
eg:signal(SIGINT,SIG_IGN);
②高级版signaction()接收信号
#include
int sigaction (int signum, const struct sigaction *act,structsigaction*oldact);
第一个参数为:捕捉的信号
第二个参数为:绑定某些功能参数的结构体
第三个参数:备份
struct sigaction {
void (*sa_handler)(int); //信号处理程序,不接手额外数据
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,接收额外数据
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
第一个参数为:函数指针1
第二个参数为:函数指针2
第三个参数为:结构体
第四个参数为:标记
如何发送信号?
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
第一个参数为:发给谁
第二个参数为:什么信号
第三个参数为:发送的消息(int或char)
五、信号量
1、信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
特点:
①信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
②信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
③每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
④支持信号量组
#include
1.创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
第二个参数为:信号量集的个数
2.对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);
3.控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);
第二个参数为:操作第几个信号量
第四个参数需定义一个联合体.
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) */
};
2、PV操作
int semop(int semid, struct sembuf *sops, unsigned nsops);
第二个参数为:配置信号量个数
第三个参数为:指针,指向第二个信号量的地址。
```c
key_t key;
key=ftok(".",1);
//int semget(key_t key, int nsems, int semflg);
int semid=semget(key,1,IPC_CREAT|0666); //获取信号量
union semun initsem;
initsem.val=0; //操作第0个信号的值
//int semctl(int semid, int semnum, int cmd, ...);
semctl(semid,0,SETVAL,initsem);//SETVAL设置信号量的值
取钥匙
```c
void pGetKey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=-1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
printf("getkey\n");
}
放钥匙
void vPutBackKey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
printf("put back the key\n");
}
标签:信号,int,void,编程,信号量,五种,key,Linux,共享内存
来源: https://blog.csdn.net/PPPPPPPKD/article/details/115655359