首頁 > 軟體

C語言中程序間通訊的方式詳解

2022-08-18 18:00:51

一.無名管道

1.1無名管道的原理

無名管道只能用於親緣間程序的通訊,無名管道的大小是64K。無名管道是核心空間實現的機制。

1.2功能

1) Pipe()建立一個管道,這是一個單向的資料通道,可用於程序間通訊。

2)陣列pipefd用於返回兩個指向管道末端的檔案描述符。

3)Pipefd[0]指的是管道的讀端。Pipefd[1]指的是管道的寫入端,寫入管道的寫入端資料由核心進行緩衝(64k),直到從管道的讀取端讀取為止。

1.3無名管道通訊特點

1.只能用於親緣間程序的通訊

2.無名管道資料半雙工的通訊的方式

單工 : A -------------->B

半雙工 : 同一時刻 A----->B B------>A

全雙工 : 同一時刻 A<---->B

3.無名管道的大小是64K

4.無名管道不能夠使用lseek函數

5.讀寫的特點

如果讀端存在寫管道:有多少寫多少,直到寫滿為止(64k)寫阻塞

如果讀端不存寫管道,管道破裂(SIGPIPE) (可以通過gdb偵錯看現象)

如果寫端存在讀管道:有多少讀多少,沒有資料的時候阻塞等待

如果寫端不存在讀管道:有多少讀多少,沒有資料的時候立即返回

1.4無名管道的範例

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h> 
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
int main(int argc, char const *argv[])
{
    pid_t pid;
    int num[2];
    char buff[128] = {0};
 
    if (pipe(num)){
        ERROR("pipe error");
    }
 
    if ((pid = fork()) == -1){
        ERROR("fork error");
    }else if(pid == 0){
        close(num[0]);
        while (1){
            memset(buff, 0, sizeof(buff));
            printf("請輸入您要輸入的數值>>");
            fgets(buff, sizeof(buff), stdin);
            buff[strlen(buff) -1] = '';
            write(num[1], buff, strlen(buff));
 
            if (!strncmp(buff, "quit", 4)){
                break;
            }
 
        }
        close(num[1]);
        exit(EXIT_SUCCESS);
    }else{
        close(num[1]);
        while (1){
            memset(buff, 0, sizeof(buff));
            read(num[0], buff, sizeof(buff));
 
            if (!strncmp(buff, "quit", 4)){
                break;
            }
            printf("%sn",buff);
        }   
        close(num[0]);
        wait(NULL);
    }
 
    return 0;
}

二.有名管道

2.1有名管道的原理

1)可以用於親緣間程序的通訊,也可以用於非親緣間的程序的通訊。

2)有名管道會建立一個檔案,需要通訊的程序開啟這個檔案,產生檔案描述符後就可以通訊了,有名管道的檔案存在記憶體上。

3)有名管道的大小也是64K,也不能使用lseek函數

2.2有名管道的特點

1.可以用於任意程序間的通訊

2.有名管道資料半雙工的通訊的方式

3.有名管道的大小是64K

4.有名管道不能夠使用lseek函數

5.讀寫的特點

如果讀端存在寫管道:有多少寫多少,直到寫滿為止(64k)寫阻塞

如果讀端不存寫管道

1.讀許可權沒有開啟,寫端在open的位置阻塞

2.讀端開啟後關閉,管道破裂(SIGPIPE) (可以通過gdb偵錯看現象)

如果寫端存在讀管道:有多少讀多少,沒有資料的時候阻塞等待

如果寫端不存在讀管道

1.寫許可權沒有開啟,讀端在open的位置阻塞

2.寫端開啟後關閉,有多少讀多少,沒有資料的時候立即返回

2.3有名管道範例

mkfifo檔案:

#include <stdio.h>
#include <stdlib.h>
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
int main(int argc, char const *argv[])
{
    if (mkfifo("./fifo",0666)){
        ERROR("mkfifo error");
    }
 
    //有名管道沒有阻塞,手動加一個阻塞
    getchar();
 
    system("rm ./fifo -rf");
    
    return 0;
}

write檔案:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
 
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
int main(int argc, char const *argv[])
{
    int fd;
    char buff[128] = {0};
    if ((fd = open("./fifo",O_WRONLY)) == -1){
        ERROR("open fifo errorn");
    }
 
    while(1){
        printf("input >");
        fgets(buff, sizeof(buff), stdin);
        buff[strlen(buff) - 1] = '';
 
        write(fd, buff, strlen(buff));
        if(!strncmp("quit",buff,4))break;
    }
    close(fd);
    return 0;
}

read檔案:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
 
#define ERROR(msg)                                          
    do                                                      
    {                                                       
        printf("%s %s %dn", __FILE__, __func__, __LINE__); 
        printf(msg);                                        
        exit(-1);                                           
    } while (0)
 
int main(int argc, char const *argv[])
{
    int fd;
    char buff[128] = {0};
    if ((fd = open("./fifo", O_RDONLY)) == -1)
    {
        ERROR("open error");
    }
 
    while (1){
        memset(buff, 0, sizeof(buff));
        read(fd, buff, sizeof(buff));
        if (!strncmp("quit",buff,4)){
            break;
        }
 
        printf("%sn",buff);
    }
    close(fd);
    return 0;
}

三.訊號

3.1訊號的概念

訊號是中斷的一種軟體模擬,中斷是基於硬體實現的,訊號是基於linux核心實現的。

使用者可以給程序發訊號,程序可以給程序發訊號,核心也可以給程序發訊號。程序對

訊號的處理方式有三種:捕捉,忽略,預設

3.2傳送訊號的函數

int raise(int sig);

功能:給自己(程序或者執行緒)發訊號

引數:

@sig:訊號號

返回值:成功返回0,失敗返回非0

int kill(pid_t pid, int sig);

功能:給程序發訊號

引數:

@pid:程序號

  • pid > 0 :給pid對應的程序發訊號
  • pid = 0 :給同組的程序發訊號
  • pid = -1:給所有的有許可權操作的程序傳送訊號,init程序除外
  • pid < -1:給-pid對應的同組的程序發訊號       

@sig:訊號號

返回值:成功返回0,失敗返回-1置位錯誤碼  

3.3常用的訊號

1.在上述的訊號中只有SIGKILL和SIGSTOP兩個訊號不能被捕捉也不能被忽略

2.SIGCHLD,當子程序結束的時候,父程序收到這個SIGCHLD的訊號

3.4範例

捕捉ctrl+c

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
void handle(int num)
{
    if (num == SIGINT){
        printf("我收到一個ctrl+c的訊號n");
    }
}
 
int main(int argc, char const *argv[])
{
    //捕捉
    if (signal(SIGINT, handle) == SIG_ERR){
        ERROR("register signal error");
    }
    //忽略
    if (signal(SIGINT,SIG_IGN) == SIG_ERR){
        ERROR("register signal error");
    }
    //預設
    if (signal(SIGINT,SIG_DFL) == SIG_ERR){
        ERROR("register signale");
    }
 
 
    while(1);
    return 0;
}

捕捉管道破裂的訊息

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
void handle(int num)
{
    if (num == SIGPIPE){
        printf("捕捉到一條管道破裂的訊息n");
    }
}
 
int main(int argc, char const *argv[])
{
    int num[2];
    char buff[32] = "123";
 
    if(pipe(num)){
        ERROR("pipe error");
    }
 
    if (signal(SIGPIPE,handle) == SIG_ERR){
        ERROR("signal error");
    }
 
    close(num[0]);
 
    write(num[1],buff,strlen(buff));
    return 0;
}

阻塞等待為子程序回收資源

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
void signal_handle(int signo)
{
    printf("我是父程序,收到了子程序退出的訊號,為它回收資源n");
    waitpid(-1,NULL,WNOHANG); //非阻塞方式回收資源
    printf("為子程序回收資源成功n");
    raise(SIGKILL);  //給父程序傳送訊號,結束父程序
}
 
int main(int argc,const char * argv[])
{
    pid_t pid;
 
    pid = fork();
    if(pid == -1){
        ERROR("fork error");
    }else if(pid == 0){
        sleep(5);
        printf("子程序執行結束了n");
        exit(EXIT_SUCCESS);
    }else{
        if(signal(SIGCHLD,signal_handle)==SIG_ERR)
            ERROR("signal error");
 
        while(1);
    }
    
    return 0;
}

用arlarm實現一個鬥地主機制

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
 
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
void handle(int num)
{
    if (num == SIGALRM){
        printf("自動出牌n");
    }
    alarm(3);
}
 
int main(int argc, char const *argv[])
{
    char ch;
    if (signal(SIGALRM,handle) == SIG_ERR){
        ERROR("signale error");
    }
    
    alarm(3);
 
    while (1){
        printf("請輸入您要出的牌>>");
        ch = getchar();
        getchar();
        printf("%cn",ch);
        alarm(3);
    }
    return 0;
}

四.IPC程序間通訊

4.1IPC程序間通訊的種類

(1)訊息佇列

(2)共用記憶體

(3)訊號燈集

4.2檢視IPC程序間通訊的命令

4.2.1檢視

ipcs -q //檢視訊息佇列的命令

ipcs -m //檢視共用記憶體的命令

ipcs -s //檢視訊號燈集的命令

4.2.2刪除ipc的命令

ipcrm -q msqid //刪除訊息佇列命令

ipcrm -m shmid //刪除共用記憶體命令

ipcrm -s semid //刪除訊號燈集的命令

4.3訊息佇列

4.3.1訊息佇列的原理

訊息佇列也是藉助核心實現的,A程序將訊息放到訊息佇列中,佇列中的訊息

有訊息的型別和訊息的正文。B程序可以根據想取的訊息的型別從訊息佇列中

將訊息讀走。訊息佇列預設的大小是16384個位元組。如果訊息佇列中的訊息滿了,

A程序還想往佇列中發訊息,此時A程序阻塞。

4.3.2IPC程序間通訊鍵值的獲取

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
 
#define ERROR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
int main(int argc, char const *argv[])
{
    key_t key;
    struct stat st;
 
   if ((key = ftok("/home/linux",'w')) == -1){
        ERROR("ftok error");
   }
 
   printf("key=%#xn",key);
 
   if (stat("/home/linux",&st)){
        ERROR("stat error");
   }
 
   printf("pro_id=%#x,devno=%#lx,ino=#=%#lxn",'w',st.st_dev,st.st_ino);
    return 0;
}

結果圖:

4.3.3訊息佇列的範例:(不關注型別的)

標頭檔案:

#ifndef __MYHEAD_H__
#define __MYHEAD_H__
 
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
 
#define  PRINT_ERR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
 
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
 
typedef  struct mubuf{
    long mtype;
    char text[512];
}msg_t;
 
#endif

傳送方:

#include "myhead.h"
 
int main(int argc, char const *argv[])
{
    key_t key;
    int msqid;
    msg_t msg ={
        .mtype = 100,
    };
 
     if((key = ftok("/home/linux/",'r'))==-1)
        PRINT_ERR("ftok get key error");
 
    if((msqid = msgget(key,IPC_CREAT|0666))==-1)
        PRINT_ERR("create msg queue error");
 
    while (1){
        memset(msg.text,0,sizeof(msg.text));
        fgets(msg.text,MSGSIZE,stdin);
        msg.text[strlen(msg.text) - 1] = '';
 
        msgsnd(msqid, &msg, MSGSIZE, 0);
 
        if (!strncmp(msg.text,"quit",4)){
            break;
        }
    }
 
    msgctl(msqid, IPC_RMID, NULL);
    return 0;
}

接受方:

#include "myhead.h"
 
int main(int argc, char const *argv[])
{
    key_t key;
    int msgqid;
    msg_t msg;
 
    if ((key = ftok("/home/linux",'r')) == -1){
        PRINT_ERR("ftok error");
    }
 
    if ((msgqid = msgget(key, IPC_CREAT|0666)) == -1){
        PRINT_ERR("msgget error");
    }
 
    while (1){
        memset(msg.text, 0, sizeof(msg.text));
        msgrcv(msgqid, &msg, MSGSIZE,0,0);
 
        if (!strncmp("quit",msg.text,4)){
            break;
        }
 
        printf("%sn",msg.text);
    }
 
    msgctl(msgqid,IPC_RMID,NULL);
    return 0;
}

4.3.4訊息佇列的範例:(關注型別的)

標頭檔案:

#ifndef __MSGQUE_H__
#define __MSGQUE_H__
 
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
 
typedef struct msgbuf {
    long id;
    char name[30];
    char sex;
    int age;
}msg_t;
 
#define MSGSIZE (sizeof(msg_t)-sizeof(long))
 
#endif

傳送方:

#include "msgqueue.h"
#include <head.h>
 
int main(int argc, const char* argv[])
{
    key_t key;
    int msqid;
 
    if ((key = ftok("/home/linux/", 'r')) == -1)
        PRINT_ERR("ftok get key error");
 
    if ((msqid = msgget(key, IPC_CREAT | 0666)) == -1)
        PRINT_ERR("create msg queue error");
 
    msg_t m1 = {
        .id = 1,
        .name = "zhangsan",
        .sex = 'm',
        .age = 30,
    };
    msgsnd(msqid, &m1, MSGSIZE, 0);
    msg_t m2 = {
        .id = 2,
        .name = "lisi",
        .sex = 'w',
        .age = 18,
    };
    msgsnd(msqid, &m2, MSGSIZE, 0);
    msg_t m3 = {
        .id = 3,
        .name = "wangwu",
        .sex = 'm',
        .age = 22,
    };
    msgsnd(msqid, &m3, MSGSIZE, 0);
 
 
    // msgctl(msqid, IPC_RMID, NULL);
 
    return 0;
}

接受方:

#include "msgqueue.h"
 
int main(int argc, const char* argv[])
{
    key_t key;
    int msqid;
    msg_t msg;
 
 
    if ((key = ftok("/home/linux/", 'r')) == -1)
        PRINT_ERR("ftok get key error");
 
    if ((msqid = msgget(key, IPC_CREAT | 0666)) == -1)
        PRINT_ERR("create msg queue error");
 
 
    memset(&msg, 0, sizeof msg);
    msgrcv(msqid, &msg, MSGSIZE, atoi(argv[1]), 0);
    printf("id=%ld,name=%s,sec=%c,age=%dn",msg.id,msg.name,msg.sex,msg.age);
 
    // msgctl(msqid, IPC_RMID, NULL);
 
    return 0;
}

結果圖:

4.4共用記憶體

4.4.1原理:

共用記憶體:在核心空間建立共用記憶體,讓使用者的A和B程序都能夠存取到。通過這塊記憶體

進行資料的傳遞。共用記憶體所有的程序間通訊中效率最高的方式(不需要來回拷貝資料),共用記憶體的大小為 4k整數倍。

4.4.2範例

接受方:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
 
#define  PRINT_ERR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    char* over;
 
    if ((key = ftok("/home/linux", 'r')) == -1){
        PRINT_ERR("ftok error");
    }
 
    if ((shmid = shmget(key, 4096, IPC_CREAT|0666)) == -1){
        PRINT_ERR("shemget error");
    }
 
    if ((over = shmat(shmid, NULL, 0)) == (void*)-1){
        PRINT_ERR("shmat error");
    }
 
    while (1){
        if (!strncmp("quit", over, 4))break;
        getchar();
 
        printf("%sn",over);
    }
 
    if (shmdt(over)){
        PRINT_ERR("shmdt error");
    }
 
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

傳送方:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
 
#define  PRINT_ERR(msg) do{
    printf("%s %s %dn", __FILE__, __func__, __LINE__);
    printf(msg);
    exit(-1); 
}while(0)
 
int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    char* over;
 
    if ((key = ftok("/home/linux", 'r')) == -1){
        PRINT_ERR("ftok error");
    }
 
    if ((shmid = shmget(key, 4096, IPC_CREAT|0666)) == -1){
        PRINT_ERR("shmget errorn");
    }
 
    if ((over = shmat(shmid, NULL, 0)) == (void*)-1){
        PRINT_ERR("shmat errorn");
    }
 
    while (1){
        printf("請輸入>>");
        fgets(over,4096,stdin);
        over[strlen(over) - 1] = '';
 
        if (!strncmp("quit",over,4)){
            break;
        }
    }
 
    if (shmdt(over)){
        PRINT_ERR("shmdt errorn");
    }
 
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

4.5訊號燈集合

號誌的原理

號誌:又叫訊號燈集,它是實現程序間同步的機制。在一個訊號燈集中可以有很多的訊號燈,這些訊號燈它們的工作相關不干擾。一般使用的時候使用的是二值訊號燈。

訊號燈集函數的封裝

sem.h

#ifndef __SEM_H__
#define __SEM_H__
 
int mysem_init(int nsems);
int P(int semid, int semnum);
int V(int semid, int semnum);
int sem_del(int semid);
 
#endif
sem.c
#include <head.h>
 
union semun {
    int val; /* Value for SETVAL */
    struct semid_ds* buf; /* Buffer for IPC_STAT, IPC_SET */
};
 
int semnum_init_value(int semid, int which, int value)
{
    union semun sem = {
        .val = value,
    };
    if (semctl(semid, which, SETVAL, sem) == -1)
        PRINT_ERR("semctl int value error");
    return 0;
}
 
//初始化訊號燈集
int mysem_init(int nsems)
{
    key_t key;
    int semid;
    // 1.通過ftok獲取鍵值
    if ((key = ftok("/home/linux/", 'g')) == -1)
        PRINT_ERR("get key error");
    // 2.如果不選擇就建立訊號燈集,如果存在返回已存在的錯誤
    if ((semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
        if (errno == EEXIST) {
            //如果已存在,這裡呼叫semget,直接返回semid
            semid = semget(key, nsems, IPC_CREAT | 0666);
        } else {
            PRINT_ERR("create sem error");
        }
    } else {
        // 3.初始化訊號燈集中的訊號燈
        for (int i = 0; i < nsems; i++) {
            semnum_init_value(semid, i, !i);
        }
    }
 
    return semid;
}
 
//申請資源
int P(int semid, int semnum)
{
    struct sembuf buf = {
        .sem_num = semnum,
        .sem_op = -1,
        .sem_flg = 0,
    };
 
    if (semop(semid, &buf, 1))
        PRINT_ERR("request resource error");
 
    return 0;
}
//釋放資源
int V(int semid, int semnum)
{
    struct sembuf buf = {
        .sem_num = semnum,
        .sem_op = 1,
        .sem_flg = 0,
    };
 
    if (semop(semid, &buf, 1))
        PRINT_ERR("free resource error");
 
    return 0;
}
//刪除訊號燈集
int sem_del(int semid)
{
    semctl(semid,0,IPC_RMID);
}

用訊號燈集實現程序同步

寫端:

#include <head.h>
#include "sem.h"
 
int main(int argc, const char* argv[])
{
    key_t key;
    int shmid,semid;
    char* waddr;
    //0.號誌的初始化
    semid = mysem_init(2);
    if(semid == -1){
        printf("sem init error");
        return -1;
    }
    // 1.獲取key
    if ((key = ftok("/home/linux", 'p')) == -1)
        PRINT_ERR("get key error");
    // 2.建立共用記憶體
    if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) == -1)
        PRINT_ERR("create share memory error");
    // 3.將共用記憶體對映到使用者空間
    if ((waddr = shmat(shmid, NULL, 0)) == (void*)-1)
        PRINT_ERR("shmat error");
    printf("waddr = %pn", waddr);
    // 4.寫操作
    while (1) {
        P(semid,0);
        printf("input > ");
        fgets(waddr, 4096, stdin);
        waddr[strlen(waddr) - 1] = '';
        if (strncmp(waddr, "quit", 4) == 0)
            break;
        V(semid,1);
    }
    // 5.取消地址對映
    if (shmdt(waddr))
        PRINT_ERR("shmdt error");
 
    // 6.刪除共用記憶體
    if (shmctl(shmid, IPC_RMID, NULL))
        PRINT_ERR("shmrm error");
    //7.刪除號誌
    sem_del(semid);
    return 0;
}

讀埠:

#include "sem.h"
#include <head.h>
int main(int argc, const char* argv[])
{
    key_t key;
    int shmid, semid;
    char* raddr;
    // 0.號誌的初始化
    semid = mysem_init(2);
    if (semid == -1) {
        printf("sem init error");
        return -1;
    }
    // 1.獲取key
    if ((key = ftok("/home/linux", 'p')) == -1)
        PRINT_ERR("get key error");
    // 2.建立共用記憶體
    if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) == -1)
        PRINT_ERR("create share memory error");
 
    // 3.將共用記憶體對映到使用者空間
    if ((raddr = shmat(shmid, NULL, 0)) == (void*)-1)
        PRINT_ERR("shmat error");
 
    printf("waddr = %pn", raddr);
    // 4.讀操作
    while (1) {
        P(semid,1);
        printf("raddr = %sn", raddr);
        if (strncmp(raddr, "quit", 4) == 0)
            break;
        V(semid,0);
    }
    // 5.取消地址對映
    if (shmdt(raddr))
        PRINT_ERR("shmdt error");
 
    // 6.刪除共用記憶體
    shmctl(shmid, IPC_RMID, NULL);
 
    //7.刪除號誌
    sem_del(semid);
    return 0;
}

以上就是C語言中程序間通訊的方式詳解的詳細內容,更多關於C語言程序通訊的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com