進程間通信及同步的方式有:pipe、fifo、共享內存、信號量、消息隊列、socket通信等
1:管道
兩個或多個進程間通過管道,可以互相傳遞信息,利用read和write系統調用函數來進行讀寫操作。
int pipe(int filedes[2]);
filedes是一個有兩個成員的整形數組。
(1)filedes[0]將用來從管道讀取數據
(2)filedes[1]用來向管道寫入數據。
如下圖所示:
示例代碼:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
int data_processed;
int file_pipes[2];
const char some_data[] = “123”;
char buffer[512];
memset(buffer, ‘\0’, sizeof(buffer));
if (pipe(file_pipes) == 0)
{
data_processed=write(file_pipes[1],some_data,strlen(some_data));
printf(“Wrote %d bytes\n”, data_processed);
data_processed = read(file_pipes[0], buffer, BUFSIZ);
printf(“Read %d bytes: %s\n”, data_processed, buffer);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
2:命名管道FIFO
(1)提供進程間數據交換的一種機制
(2)與pipe不同的是,命名管道不需要程序由一個共同的祖先進程啓動
(3)命名管道是一種特殊類型的文件,因爲是文件,具備了和文件相同的特點,有文件名、所有者,訪問權限等,但行爲和管道相同
2.1:命令行方式創建FIFO管道
$ mknod filename p
$ mkfifo filename
2.2:程序中創建FIFO管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);
參數filename爲指定管道文件的名字
參數mode給出了FIFO的訪問權限
例程:創建命名管道FIFO
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int res = mkfifo(“/tmp/my_fifo”, 0777);
if (res == 0)
printf(“FIFO created\n”);
exit(EXIT_SUCCESS);
}
2.3:打開FIFO文件
與通過pipe調用創建管道不同,FIFO是以命名文件的形式存在,而不是打開的文件描述符,所以對它進行讀寫操作之前必須先打開它。
FIFO也用open和close函數打開和關閉,但多了額外的功能
例:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#define FIFO_NAME “/tmp/my_fifo”
int main() {
int res;
int open_mode = 0;
open_mode = O_RDONLY | O_NONBLOCK;
res=open(FIFO_NAME, open_mode);
if (res == -1)
{
perror(“Failed tp open FIFO file”);
exit(1);
}
printf(“FIFO file is opened\n”);
clsoe(res);
return 0;
}
注:注意:打開管道文件,可以選擇阻塞與非阻塞兩種方式。
例程:FIFO讀、寫程序
讀:
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo“
int main()
{
char buf[100];
int fd;
int nread;
fd = open(FIFO_NAME, O_RDONLY ,0);
if (fd == -1)
{
perror("open");
exit(1);
}
memset(buf,0,sizeof(buf));
if ((nread = read(fd, buf, 100))==-1)
{
perror("read");
exit(1);
}
printf("read %s from FIFO\n",buf);
return 0;
}
寫:
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo“
int main()
{
int fd;
int nwrite;
char buf[100];
fd = open(FIFO_NAME,O_WRONLY ,0);
if (fd == -1)
{
perror("Failed to open FIFO file");
}
printf("FIFO file is opened\n");
strcpy(buf,"helloworld");
if ((nwrite=write(fd,buf,100))==-1)
{
perror("write");
exit(1);
}
close(fd);
return 0;
}
3:信號量
(1)確保程序對某個特定的資源具有獨佔式的訪問的機制
(2)信號量是特殊的變量,只能取正整數並且只允許有兩種操作:等待P和信號V
3.1:IPC機制關鍵值
#include <sys/ipc.h>
key_t ftok(const char *path, int id);
函數的作用是以path相關的信息爲基礎返回一個關鍵值,利用此關鍵值實現IPC同步和通信機制,也可使用IPC_PRIVATE作爲參數由系統自動生成。
ftok與IPC_PRIVATE的區別:
ftok藉助文件實現功能,常在不同的進程中使用,尤其是兩個進程不是父子進程時,但因爲是藉助文件實現,一旦文件被刪除後重新創建,獲得的key就會發生變化,影響兩個進程同步及通信機制的實現
IPC_PRIVATE由系統自動生成鍵值,常用於父子進程中,如要運行非父子進程關係的兩個進程間,需要傳值才能讓另外的進程獲得IPC_PRIVATE的值
3.2:semget函數:創建信號量集
#include <sys/sem.h>
int semget(key_t key, int nsems, int sem_flags);
參數key:整數,相關進程可以通過它訪問一個信號量集
參數nsems:指定一個信號量集裏的信號量數量。它幾乎總是取值1
參數sem_flags:是一組標誌,與open函數的標誌相似
3.3:semop函數:實際執行各種信號量操作
#include <sys/sem.h>
int semop(int semid, struct sembuf * op_array, size_t num_ops);
參數semid:爲semget函數返回的信號量集標識符
參數op_array:對應一個或多個信號量的sembuf結構體操作
參數num_ops:同時對幾個信號量進行操作
sembuf結構體數據成員說明
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
}
sem_num: 對信號量集中的哪個信號量進行操作,從0開始表示
sem_op: 操作數設置,通常是+1或-1操作
sem_flg: 功能設置,IPC_NOWAIT與SEM_UNDO
3.4:semctl 函數:在一組信號量上做各種控制操作,諸如信號量集合的初始化、刪除和狀態查詢等
#include <sys/sem.h>
int semctl(int semid, int sem_num, int command, union semun ctl_arg);
參數semid:爲semget的返回值
參數sem_num:指定特定的信號量
參數ctl_arg 是一個聯合體,對應三種不同功能的semctl調用
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}
參數command:有三種功能:
(1)標準的IPC函數:
IPC_STAT : 把狀態信息放入ctl_arg.stat中
IPC_SET : 用ctl_arg.stat中的值設置所有權/許可權
IPC_RMID : 從系統中刪除信號量集合
(2)單信號量操作:
GETVAL : 返回信號量的值
SETVAL : 把信號量的值寫入ctl_arg.val中
GETPID : 返回sempid值
GETNCNT : 返回semncnt
GETZCNT : 返回semzcnt
(3)全信號量操作:
GETALL : 把所有信號量的semvals值寫入ctl_arg.array
SETALL : 用ctl_arg.array中的值設置所有信號量的semvals
3.5例子:
(1)創建信號量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int semid;
int nsems = 1;
int flags = 0666;
struct sembuf buf;
semid = semget(IPC_PRIVATE, nsems, flags);
if (semid < 0) {
perror(“semget”);
exit(EXIT_FAILURE);
}
printf(“semphore created: %d\n”, semid);
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = IPC_NOWAIT;
if ((semop(semid, &buf, nsems)) < 0) {
perror(“semop”);
exit(EXIT_FAILURE);
}
system(“ipcs –s”);
exit(EXIT_SUCCESS);
}
(2)刪除信號量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int semid;
if (argc !=2)
{
puts(“USAGE:sctl <semaphore id>”);
exit(EXIT_FAILURE);
}
semid = atoi(argv[1]);
if((semctl(semid, 0, IPC_RMID)) < 0)
{
perror(“semctl IPC_RMID”);
exit(EXIT_FAILURE);
}
else
{
puts(“semaphore removed”);
system(“ipcs –s”);
}
exit(EXIT_SUCCESS);
}
(3)進程同步操作
#include <sys/types.h>
#include <linux/sem.h>
#include <unistd.h>
int pid,semid;
struct sembuf P,V;
union semun arg;
int main(void)
{
arg.val = 1;
semid = semget(IPC_PRIVATE,1,0666|IPC_CREAT);
semctl(semid, 0, SETVAL, arg);
P.sem_num = 0;
P.sem_op = -1;
P.sem_flg = SEM_UNDO;
V.sem_num = 0;
V.sem_op = 1;
V.sem_flg = SEM_UNDO;
if ((pid = fork()) == -1)
{
perror(“fork”);
exit(1);
}
else if (pid == 0)
{
semop(semid,&P,1);
system(“echo –n ‘a’ >> ./printer);
system(“echo –n ‘b’ >> ./printer);
system(“echo ‘c’ >> ./printer);
semop(semid,&V,1);
}
else
{
semop(semid,&P,1);
system(“echo –n ‘1’ >> ./printer);
system(‘echo –n ‘2’ >> ./printer);
system(‘echo ‘3’ >> ./printer);
semop(semid,&V,1);
}
semctl(semid, IPC_RMID,0);
exit(0);
}
4:消息隊列
4.1:msgget函數:創建和訪問消息隊列
#include<sys/msg.h>
int msgget(key_t key, int permflags);
參數key:爲標識好的消息隊列
參數permflags:有兩個值可選擇:
IPC_CREAT : 創建新的隊列
IPC_EXCL : 與IPC_CREAT合用,如要創建的隊列已存在,返回值爲-1
例:創建消息隊列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int qid;
key_t key;
key = 123;
if ((qid = msgget(key, IPC_CREAT | 0666)) < 0)
{
perror(“msgget:create”);
exit(EXIT_FAILURE);
}
printf(“created queue id = %d\n”, qid);
if ((qid == msgget(key, 0)) < 0) {
perror(“msgget:open”);
exit(EXIT_FAILURE);
}
printf(“opened queue id = %d\n”, qid);
exit(EXIT_SUCCESS);
}
4.2:
msgsnd函數:功能爲向消息隊列添加信息
msgrcv函數:功能爲從隊列中讀取消息
#include <sys/msg.h>
int msgsnd(int mqid, const void * message, size_t size, int flags);
int msgrcv(int mqid, void * message, size_t size, long msg_type,int flags);
參數mqid:由msgget返回獲得的
參數message:由用戶自定義的結構
參數flags:只有一個有意義的值IPC_NOWAIT,當消息無法返回,調用立即返回,返回值爲-1
參數msg_type:決定實際將讀到的是哪條消息
4.3:msgctl函數:功能爲使進程獲取有關消息隊列的狀態信息、改變與隊列有關的限制或從系統刪除一個隊列
int msgctl(int mqid, int command, struct msqid_ds *msq_stat);
參數mqid:有效的隊列標識符
參數command:有三個值
IPC_STAT : 告訴系統把狀態信息存入msq_stat中
IPC_SET : 用於通過msq_stat中存放的信息爲消息隊列設置控制變量的值
IPC_RMID : 從系統中刪除消息隊列
參數msq_stat:指向msqid_ds結構的指針
例程:發送與接收
#include <sys/types.h>
#include <linux/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int qid, len;
struct msgbuf sndmsg;
struct msgbuf rcvmsg;
if ((qid = msgget(IPC_PRIVATE, IPC_CREAT|0666)) == -1)
{
perror(“msgget”);
exit(1);
}
sprintf(sndmsg.mtext, “hello”);
sndmsg.mtype = 1;
len = strlen(sndmsg.mtext);
if ((msgsnd(qid, &sndmsg, len, 0)) < 0)
{
perror(“msgsnd”);
exit(1);
}
puts(“message posted”);
sleep(1);
if ((msgrcv(qid, &rcvmsg, len, 0, 0)) < 0)
{
perror(“msgrcv”);
exit(1);
}
puts(“message received”);
rcvmsg.mtext[5]=‘\0\;
printf(“This message is %s\n”,rcvmsg.mtext);
msgctl(qid, IPC_RMID, NULL);
exit(0);
}
5:共享內存
(1)共享存儲操作使得兩個或兩個以上的進程可以共用一段物理內存
(2)一般情況下,兩個進程的數據區是完全獨立的,通常它在所有的IPC機制中是最有效的
5.1:shmget函數:創建一段內存用於進程共享數據
int shmget(key_t key, size_t size, int permflags);
參數key:內存段的關鍵值
參數size:內存段所需最小字節數
參數permflags:給出了內存段的訪問權限
例程:創建共享內存
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFSZ 4096
int main(void)
{
int shmid;
if((shmid = shget(IPC_PRIVATE, BUFSZ,0666)) < 0)
{
perror(“shmget”);
exit(EXIT_FAILURE);
}
printf(“segment created: %d\n”, shmid);
system(“ipcs –m”);
exit(EXIT_SUCCESS);
}
5.2:shmat函數:功能爲將標識的內存段掛接到調用進程的有效地址上
void *shmat(int shmid, const void *daddr, int shmflags);
參數shmid:表示的共享內存段
參數daddr:控制調用選擇的地址
參數shmflags有2個值:
SHM_RDONLY: 使內存段只讀掛接
SHM_RND : 使掛接的地址可以在daddr的一頁範圍內變動
如不設置,shmat將精確使用daddr的值
5.3:shmdt函數:取消共享內存的掛接
int shmdt(char *shmadr);
參數shmadr:取消掛接的共享內存
5.4:shmctl函數:功能爲對共享內存進行控制管理
int shmctl(int shmid, int command, struct shmid_ds * shm_stat);
功能使用與msgctl函數類似,其中command參數可以取以下參數:
IPC_STAT 把shmid_ds結構中的數據設置爲共享內存當前關聯值
IPC_SET 進程有足夠權限,就設置成shmid_ds中給出的值
IPC_RMID 刪除共享內存段
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
5.5:例:共享內存
#include <stdio.h>
#include <sys/shm.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int shmid;
char *shmbuf;
pid_t pid;
key_t key;
shmid = shmget(IPC_PRIVATE, 100, IPC_CREAT|0777);
if (shmid == -1)
{
perror("shmid");
exit(1);
}
shmbuf = shmat(shmid, NULL, 0);
if (shmbuf == (void*) -1)
{
perror("shmat");
exit(1);
}
pid=fork();
if(pid == -1)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
strcpy(shmbuf,"this is child");
}
else
{
printf("%s\n",shmbuf);
wait(NULL);
shmdt(shmid);
shmctl(shmid, IPC_RMID, NULL);
}
exit(0);
}
_