C语言中进程间通讯的方式详解

目录
  • 一.无名管道
    • 1.1无名管道的原理
    • 1.2功能
    • 1.3无名管道通信特点
    • 1.4无名管道的实例
  • 二.有名管道
    • 2.1有名管道的原理
    • 2.2有名管道的特点
    • 2.3有名管道实例
  • 三.信号
    • 3.1信号的概念
    • 3.2发送信号的函数
    • 3.3常用的信号
    • 3.4实例
  • 四.IPC进程间通信
    • 4.1IPC进程间通信的种类
    • 4.2查看IPC进程间通信的命令
    • 4.3消息队列
    • 4.4共享内存
    • 4.5信号灯集合

一.无名管道

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 %d\n", __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] = '\0';
            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("%s\n",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 %d\n", __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 %d\n", __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 error\n");
    }

    while(1){
        printf("input >");
        fgets(buff, sizeof(buff), stdin);
        buff[strlen(buff) - 1] = '\0';

        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 %d\n", __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("%s\n",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 %d\n", __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 %d\n", __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 %d\n", __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("%c\n",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 %d\n", __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=%#x\n",key);

   if (stat("/home/linux",&st)){
        ERROR("stat error");
   }

   printf("pro_id=%#x,devno=%#lx,ino=#=%#lx\n",'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 %d\n", __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] = '\0';

        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("%s\n",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=%d\n",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 %d\n", __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("%s\n",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 %d\n", __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 error\n");
    }

    if ((over = shmat(shmid, NULL, 0)) == (void*)-1){
        PRINT_ERR("shmat error\n");
    }

    while (1){
        printf("请输入>>");
        fgets(over,4096,stdin);
        over[strlen(over) - 1] = '\0';

        if (!strncmp("quit",over,4)){
            break;
        }
    }

    if (shmdt(over)){
        PRINT_ERR("shmdt error\n");
    }

    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 = %p\n", waddr);
    // 4.写操作
    while (1) {
        P(semid,0);
        printf("input > ");
        fgets(waddr, 4096, stdin);
        waddr[strlen(waddr) - 1] = '\0';
        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 = %p\n", raddr);
    // 4.读操作
    while (1) {
        P(semid,1);
        printf("raddr = %s\n", 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语言进程通讯的资料请关注我们其它相关文章!

(0)

相关推荐

  • C/C++ 进程通讯(命名管道)的实例

    服务端代码: // pipe_server.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdio.h> #include <windows.h> #include <ctime> int main(int argc, _TCHAR* argv[]) { srand(time(NULL)); char buf[256] = ""; DWORD rlen = 0; HA

  • C语言实现进程间通信原理解析

    最近学习了操作系统的并发:以下是关于进程间实现并发,通信的两个方法. 1:利用管道进行进程间的通信 用到下列函数 pipe() from unistd.h sleep() write(), read() fork(); //创建子进程 管道只能用于具有亲缘关系的进程,可以将其看作一个文件,但有别于普通的文件, 管道一次只可以被一个进程访问,能实现互斥: pipe(int fd[] ), 其参数为长度为2的int数组,分别代表读端fd[0], 写端fd[1], 在创建管道后,f d[0],fd[1

  • C语言中进程间通讯的方式详解

    目录 一.无名管道 1.1无名管道的原理 1.2功能 1.3无名管道通信特点 1.4无名管道的实例 二.有名管道 2.1有名管道的原理 2.2有名管道的特点 2.3有名管道实例 三.信号 3.1信号的概念 3.2发送信号的函数 3.3常用的信号 3.4实例 四.IPC进程间通信 4.1IPC进程间通信的种类 4.2查看IPC进程间通信的命令 4.3消息队列 4.4共享内存 4.5信号灯集合 一.无名管道 1.1无名管道的原理 无名管道只能用于亲缘间进程的通信,无名管道的大小是64K.无名管道是内

  • Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号("")或反引号(``)定义. 双引号:"", 用于单行字符串. 反引号:``,用于定义多行字符串,内部会原样解析. 示例: // 单行 "心有猛虎,细嗅蔷薇" // 多行 ` 大风歌 大风起兮云飞扬. 威加海内兮归故乡. 安得猛士兮守四方! ` 字符串支持转义

  • Go语言中循环语句使用的示例详解

    目录 一.概述 1. 循环控制语句 2. 无限循环 二.Go 语言 for 循环 1. 语法 2. for语句执行过程 3. 示例 4. For-each range 循环 三.循环嵌套 1. 语法 2. 示例 四.break 语句 1. 语法 2. 示例 五. continue 语句 1. 语法 2. 示例 六.goto 语句 1. 语法 2. 示例 一.概述 在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句. 循环程序的流程图: Go 语言提供了以下几种类型循环

  • Go语言中函数可变参数(Variadic Parameter)详解

    目录 基本语法 示例一:函数中获取可变参数 示例二:将切片传给可变参数 示例三:多参数 基本语法 在Python中,在函数参数不确定数量的情况下,可以使用如下方式动态在函数内获取参数,args实质上是一个list,而kwargs是一个dict def myFun(*args, **kwargs): 在Go语言中,也有类似的实现方式,只不过Go中只能实现类似*args的数组方式,而无法实现**kwargs的方式.实现这种方式,其实也是利用数组的三个点表达方式,我们这里来回忆一下. 关于三个点(…)

  • C语言中文件常见操作的示例详解

    目录 文件打开和关闭 文件写入 文件读取 fseek函数 ftell函数 Demo示例 解决读取乱码 FILE为C语言提供的文件类型,它是一个结构体类型,用于存放文件的相关信息.文件打开成功时,对它作了内存分配和初始化. 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节. 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便. 文件打开和关闭 C语言的安全文件打开函数为_wfopen_s和_fopen_s

  • C语言中枚举与指针的实例详解

     C语言中枚举与指针的实例详解 总结一下, 定义枚举,用typedef enum关键字, 比如 typedef enum{Red,Green,Blue} Color3; 枚举到数值的转换,如果没有指定代表数值就是从0开始算, 比如 Color3 c=Red; printf("%d",c);会显示0, 除非指定 如typedef enum{Red=3,Green=5,Blue=10} Color3; 关于类型指针的定义, 定义的时候在变量名左边加*代表此变量只是一个空指针而已, 若需要赋

  • Vue-CLI项目中路由传参的方式详解

    一.标签传参方式:<router-link></router-link> 第一种 router.js { path: '/course/detail/:pk', name: 'course-detail', component: CourseDetail } 传递层 <!-- card的内容 { id: 1, bgColor: 'red', title: 'Python基础' } --> <router-link :to="`/course/detail

  • C语言中关于动态内存分配的详解

    目录 一.malloc 与free函数 二.calloc 三.realloc 四.常见的动态内存的错误 [C语言]动态内存分配 本期,我们将讲解malloc.calloc.realloc以及free函数. 这是个动态内存分配函数的头文件都是 <stdlib.h>. c语言中动态分配内存的函数,可能有些初学c语言的人不免要问了:我们为什么要通过函数来实现动态分配内存呢? 首先让我们熟悉一下计算机的内存吧!在计算机的系统中大致有这四个内存区域: 1)栈:在栈里面储存一些我们定义的局部变量以及形参(

  • Spring之@Aspect中通知的5种方式详解

    目录 @Before:前置通知 案例 对应的通知类 通知中获取被调方法信息 JoinPoint:连接点信息 ProceedingJoinPoint:环绕通知连接点信息 Signature:连接点签名信息 @Around:环绕通知 介绍 特点 案例 对应的通知类 @After:后置通知 介绍 特点 对应的通知类 @AfterReturning:返回通知 用法 特点 案例 对应的通知类 @AfterThrowing:异常通知 用法 特点 案例 对应的通知类 几种通知对比 @Aspect中有5种通知

  • TypeScript中extends的正确打开方式详解

    目录 前言 extends第一式:继承 类继承类 接口继承接口 接口继承类 extends第二式:三元表达式条件判断 普通的三元表达式条件判断 情况一:Type1和Type2为同一种类型. 情况二:Type1是Type2的子类型. 情况三: Type2类型兼容类型Type1. 带有泛型的三元表达式条件判断 extends第三式:泛型约束 前言 最近完整地看了一遍TypeScript的官方文档,发现文档中有一些知识点没有专门讲解到,或者是讲解了但却十分难以理解,因此就有了这一系列的文章,我将对没有

随机推荐