POSIX IPC
POSIX IPC
中的xxx_open()
中的一些意義相同參數的取值: xxx可以是mq
(消息隊列)、sem
(信號量)、shm
(共享內存)
參數oflag
:
O_RDONLY - 可讀
O_WRONLY - 可寫
O_RDWR - 可讀可寫
O_CREAT - 隊列不存則創建,需要使用mode和attr參數
如果文件存在,mode和attr參數被忽略
O_EXCL - 如果使用O_CREAT,消息隊列存在則創建失敗
O_NONBLOCK - 非阻塞模式打開
參數mode
:
S_IRWXU —— 文件擁有者可讀可寫可執行
S_IRUSR —— 用戶可讀
S_IWUSR —— 用戶可寫
S_IXUSR —— 用戶可執行
S_IRWXG —— 文件擁有者所在組成員可讀可寫可執行
S_IRGRP —— 組用戶可讀
S_IWGRP —— 組用戶可寫
S_IXGRP —— 組用戶可寫
S_IRWXO —— 其他用戶擁有可讀可寫可執行權限
S_IROTH —— 其他用戶可讀
S_IWOTH —— 其他用戶可寫
S_IXOTH —— 其他用戶可執行
消息隊列
消息隊列也具備描述符,消息隊列的描述符與文件的描述符類似
消息隊列的描述符是一個進程級別的句柄
子進程會繼承父進程打開的消息隊列描述符
創建或打開消息隊列
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
name —— 消息隊列名字(消息隊列標識)
oflag —— 位掩碼,mq_open()操作選項,與文件的open()的flag參數差不多:
O_RDONLY - 可讀
O_WRONLY - 可寫
O_RDWR - 可讀可寫
O_CREAT - 隊列不存則創建,需要使用mode和attr參數
如果文件存在,mode和attr參數被忽略
O_EXCL - 如果使用O_CREAT,消息隊列存在則創建失敗
O_NONBLOCK - 非阻塞模式打開
mode —— 消息隊列權限掩碼,與文件的權限掩碼一致,但執行權限無效
attr —— 消息隊列的屬性,用於指定消息隊列的最大數量和最大大小
struct mq_attr {
long mq_flags; /* oflag */
long mq_maxmsg; /* 消息隊列消息最大數量 */
long mq_msgsize; /* 每條消息的大小 */
long mq_curmsgs; /* 消息隊列中當前存在的消息數量 */
};
RETURN VALUE
成功返回消息隊列描述符,失敗返回-1
關閉消息隊列
#include <mqueue.h>
int mq_close(mqd_t mqdes);
mqdes —— 消息隊列描述符
RETURN VALUE
成功返回0, 失敗返回-1
刪除消息隊列
刪除name
標識的消息隊列,標記其在所有使用它的進程使用完後銷燬該消息隊列
#include <mqueue.h>
int mq_unlink(const char *name);
name —— 消息隊列名字
RETURN VALUE
成功返回0,失敗返回-1
獲取消息隊列的屬性
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqdes —— 消息隊列描述符
attr —— 消息隊列屬性
RETURN VALUE
成功返回0,失敗返回-1
設置消息隊列的屬性
#include <mqueue.h>
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
mqdes —— 消息隊列描述符
newattr —— 用來設置的消息隊列屬性
oldattr —— 獲取消息隊列的舊屬性
RETURN VALUE
成功返回0,失敗返回-1
發送消息
#include <time.h>
#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
//發送消息數量達到attr字段mq_maxmsg進程將阻塞
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
mqdes —— 消息隊列描述符
msg_ptr —— 發送的消息
msg_len —— 消息的大小
msg_prio —— 消息的優先級,posix的消息隊列可以設置優先級,
優先級高的消息永遠放在隊列靠前的位置
mq_timedsend()的行爲與mq_send()類似,
但如果隊列已滿,並且沒有爲消息隊列描述啓用O_NONBLOCK標誌,
則將abs_timeout指向指定調用將阻塞多長時間的結構
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
RETURN VALUE
成功返回0, 失敗返回-1
接收消息
#include <mqueue.h>
#include <time.h>
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio,
const struct timespec *abs_timeout);
mqdes —— 消息隊列描述符
msg_ptr —— 發送的消息
msg_len —— 消息的大小,必須大於消息隊列屬性attr中mq_msgsize字段
msg_prio —— 消息的優先級
mq_timedreceive()的行爲與mq_receive()類似,但如果隊列是空的,
並且沒有爲消息隊列描述啓用O_NONBLOCK標誌,
則將abs_timeout指向指定調用將阻塞多長時間的結構
RETURN VALUE
成功返回接收到的數據大小,失敗返回-1
代碼
/**********************************************************************
* @file: xxx.c
* @author: guangjieMVP
* @version: v1.00.00
* @date: 2020-xx-xx
* @github: https://github.com/guangjieMVP
* @brief:
************************************************************************/
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define handler_err(err) \
do{ perror(err); exit(0); } while(0)
char father_buf[] = "I am your father";
int main(int argc, char **argv)
{
struct mq_attr m_attr;
m_attr.mq_maxmsg = 10;
m_attr.mq_msgsize = 40;
mqd_t mq_fd = mq_open("/mq", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, &m_attr);
if (mq_fd < 0)
{
handler_err("mq_open");
}
struct mq_attr get_attr;
int ret = mq_getattr(mq_fd, &get_attr);
if (ret < 0)
{
mq_close(mq_fd);
handler_err("mq_getattr");
}
printf("maxmsg = %ld, msgsize = %ld\r\n", get_attr.mq_maxmsg, get_attr.mq_msgsize);
pid_t pid = fork();
if (pid == 0)
{
unsigned int msg_prio = 0;
char msg_ptr[100];
while(1)
{
if (mq_receive(mq_fd, msg_ptr, sizeof(msg_ptr), &msg_prio) < 0)//msg_ptr的大小必須大於或等於m_attr.mq_msgsize
{
handler_err("mq_receive");
}
printf("%s\r\n", msg_ptr);
}
}
else
{
while (1)
{
if ( mq_send(mq_fd, father_buf, sizeof(father_buf), 1) < 0)
{
handler_err("mq_send");
}
printf("father is running\r\n");
sleep(1);
}
}
mq_close(mq_fd);
return 0;
}
信號量
進程或線程間同步、互斥訪問共享資源
信號量屬於系統範疇,進程退出也不會自動銷燬,需要手動刪除釋放資源
使用信號量的程序編譯時需要鏈接pthread
庫:在編譯時加上下面編譯選項
-pthread
信號量類型
-
有名信號量
擁有名字,不同進程或線程之間可以通過名字訪問同一信號量
-
無名信號量
特點:
輕量化、基於內存(不存在於任何文件系統中)
多用於進程內部線程間的同步互斥
沒有名字,存放在內存預先指定位置
可以在進程或線程之間共享:
進程之間共享時,必須位於進程共享內存區域中
線程之間共享,信號量存放於線程共享內存區域中
有名信號量
打開or創建信號量
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
name —— 信號量名字
oflag —— 信號量打開選項掩碼
mode —— 信號量的權限
value —— 信號量的初始化值
RETURN VALUE
成功返回信號量的句柄,失敗返回SEM_FAILED
關閉信號量
#include <semaphore.h>
int sem_close(sem_t *sem);
sem —— 信號量句柄
RETURN VALUE
成功返回0,失敗返回-1
刪除信號量
#include <semaphore.h>
int sem_unlink(const char *name);
name —— 信號量名字
RETURN VALUE
成功返回0; 失敗返回-1
釋放信號量
#include <semaphore.h>
int sem_post(sem_t *sem);
sem —— 信號量句柄
RETURN VALUE
成功返回0,失敗返回 -1,信號量的值不變
等待信號量
#include <semaphore.h>
int sem_wait(sem_t *sem);
sem —— 信號量句柄
RETURN VALUE
成功返回0,失敗返回 -1,信號量的值不變
獲取信號量當前值
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
sem —— 信號量句柄
sval —— 存放獲取的信號量值
RETURN VALUE
成功返回0,失敗返回 -1,
代碼
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define handler_err(err) \
do{ perror(err);} while(0)
#define SEM_NAME "/name_sem" //信號量的名字
int main(int argc ,char **argv)
{
sem_t *name_sem = sem_open(SEM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, 0);
if (name_sem == SEM_FAILED)
{
handler_err("sem_open");
exit(0);
}
pid_t pid = fork();
if (pid == 0)
{
int ret;
while (1)
{
ret = sem_wait(name_sem);
if (ret == 0)
{
printf("child is running\r\n");
}
}
}
else
{
char buf[100];
char *ret;
while (1)
{
ret = fgets(buf, sizeof(buf), stdin);
if (ret != NULL)
{
printf("father : %s\r\n", buf);
}
if (strstr(buf, "post") != NULL)
{
sem_post(name_sem); //釋放信號量
}
}
sem_close(name_sem);
sem_unlink(SEM_NAME);
}
return 0;
}
無名信號量
一般多用於進程的線程間通信
初始化無名信號量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem —— 信號量句柄
pshared —— 無名信號量共享方式:
pshared = 0,同一個進程的線程之間共享
pshared != 0, 不同進程之間之間共享,無名信號量必須存在共享內存區域(POSIX共享內存、system V共享內存、內存映射)
value —— 信號量的初始化值
RETURN VALUE
成功返回0; 失敗返回-1。
銷燬無名信號量
不存在進程或者線程等待時才能夠安全銷燬信號量
#include <semaphore.h>
int sem_destroy(sem_t *sem);
sem —— 信號量句柄
RETURN VALUE
成功返回0; 失敗返回-1。
代碼
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#define handler_err(err) \
do{ perror(err); } while(0)
int main(int argc ,char **argv)
{
sem_t *p_sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); //MAP_ANONYMOUS 匿名映射
if (p_sem != MAP_FAILED){
printf("sem addr : %p\r\n", p_sem);
}else{
handler_err("mmap");
exit(0);
}
if (sem_init(p_sem, 2, 0) < 0) { //初始化無名信號量
handler_err("sem_open");
exit(0);
}
pid_t pid = fork();
if (pid == 0){
int ret;
int sem_val;
int cnt = 0;
while (1){
ret = sem_wait(p_sem);
if (ret == 0){
printf("child is running\r\n");
}else{
perror("sem_wait");
}
sem_getvalue(p_sem, &sem_val);
printf("sem val : %d\r\n", sem_val);
printf("cnt : %d\r\n", cnt++);
}
}else{
char buf[100];
char *ret;
int sem_val;
while (1){
ret = fgets(buf, sizeof(buf), stdin);
if (ret != NULL){
// printf("father : %s\r\n", buf);
}
if (strstr(buf, "post") != NULL){
if (sem_post(p_sem) < 0){ //釋放信號量
perror("sem_post");
}
sem_getvalue(p_sem, &sem_val);
printf("sem val : %d\r\n", sem_val);
}
}
}
sem_close(p_sem);
sem_destroy(p_sem);
return 0;
}
共享內存
共享內存的使用一般要加上互斥,防止多進程寫入數據造成數據踐踏
使用步驟:
1、shm_open()
打開一個共享內存對象
2、ftruncate()
設置共享內存對象大小
3、mmap()
使用flags
指定MAP_SHARED
進行內存映射
shm_open()
、mmap()
分別類似system V
共享內存的shmget()
、shmat()
操作
創建共享內存對象
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_open(const char *name, int oflag, mode_t mode);
name —— 共享內存對象名字
oflag —— 打開方式選項
mode —— shm權限
RETURN VALUE
成功返回非負的文件描述符,失敗返回-1
新創建的共享內存對象長度會被設置爲0
,需調用**ftruncate()**函數設置長度,新增加的字節的值被初始化爲0
設置共享內存對象的長度
#include <unistd.h>
#include <sys/types.h>
int ftruncate(int fd, off_t length);
fd —— 描述符(文件描述符、共享內存描述符等)
length —— 設置的長度
RETURN VALUE
成功返回0,失敗返回-1
刪除共享內存對象
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_unlink(const char *name);
name —— 共享內存對象名字
RETURN VALUE
成功返回0,失敗返回-1
/**********************************************************************
* @file: xxx.c
* @author: guangjieMVP
* @version: v1.00.00
* @date: 2020-xx-xx
* @github: https://github.com/guangjieMVP
* @brief:
************************************************************************/
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/types.h>
#define handler_err(err) \
do{ perror(#err); } while(0)
#define SHM_NAME "/shm_test"
#define SEM_NAME "/name_sem"
int main(int argc ,char **argv)
{
int shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG); //打開共享內存
if (shm_fd < 0){
handler_err(shm_open);
exit(0);
}
if (ftruncate(shm_fd, 1024) < 0){ //設置共享內存大小
handler_err(ftruncate);
exit(0);
}
struct stat shmstat;
int ret = fstat(shm_fd, &shmstat); //獲取共享內存信息
if (ret < 0){
handler_err(fstat);
exit(0);
}
printf("shm size : %ld\r\n", shmstat.st_size);
//內存映射
char *p = mmap(NULL, shmstat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (p == MAP_FAILED){
handler_err(mmap);
exit(0);
}
sem_t *name_sem = sem_open(SEM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, 0);//打開信號量
if (name_sem == SEM_FAILED){
handler_err(sem_open);
exit(0);
}
pid_t pid = fork();
if (pid == 0){
while(1){
ret = sem_wait(name_sem);
if (ret == 0) {
printf("child is running\r\n");
}
printf("%s\r\n", p);
}
}else{
int cnt = 0;
// char buf[40];
while(1){
sprintf(p, "Hello, Linux %d\r\n", cnt++);
sem_post(name_sem); //釋放信號量
sleep(1);
}
}
if ( shm_unlink(SHM_NAME) < 0){
handler_err(shm_unlink);
}
return 0;
}