• Linux学习之进程通讯(IPC)—进程之间的通信


    前言

    本文记录进程之间不同通讯方式的学习过程以及程序案例,学习过程中得益于以下两篇优秀博文:

    凉了!张三同学没答好「进程间通信」,被面试官挂了…(图文并茂,言简意骇,有助于概念理解)
    进程间通信的六种常见方式(逻辑清晰,干净整洁,有程序源码)

    在这里插入图片描述

    一、管道

    管道使用简单,但效率较低不适用于进程之间的频繁信息交流

    1.1匿名管道

    匿名管道pipe 是一种半双工的通信方式,只能在具有亲缘关系的进程间使用.进程的亲缘关系一般指的是父子关系。使用写入端口时关闭读取端口,使用读取端口时关闭写入端口,可以使用普通的read、write等函数进行读写操作,操作时遵循先进先出原则。

    1.1.1 pipe 原型

       	#include 
    
        int pipe(int pipefd[2]); 
    
    • 1
    • 2
    • 3

    1.1.2 pipe 实例

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //匿名管道的创建与使用
    int main()
    {
            int fd[2];
            char buf[1024]={""};
            int pid;
            int states;
    
            if(pipe(fd)==-1)
            {
                    printf("fail!\n");
            }
    
            pid=fork();
    
            if(pid<0)
            {
                    printf("creat child fail!\n");
            }
            else if(pid>0)//父进程
            {
                    printf("This is Father\n");
                    sleep(3);
                    close(fd[0]);//管道0负责读 1负责写
                    write(fd[1],"Hello from father",strlen("Hello from father"));
                    wait(&states);
            }
            else//子进程
            {
                    printf("This is child\n");
                    close(fd[1]);
                    read(fd[0],buf,1000);
                    printf("read from father:%s\n",buf);
                    exit(0);
            }
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    1.2 命名管道

    命名管道FIFO,以一种特殊设备文件形式存在于文件系统中,有路径名与之相关联。因此,可以在无关的进程之间交换数据。

    1.2.1 fifo 原型

        #include 
    
        #include 
    
        int mkfifo(const char *pathname, mode_t mode);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.2.2 fifo 实例

    进程向管道写入

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //有名管道的写入
    int main()
    {
            char *str="message from fife";
            int cnt=0;
            int fd=open("flie",O_WRONLY);
            printf("write open success\n");
    
            while(1)
            {
    
                    sleep(1);
                    write(fd,str,strlen(str));
                    if(cnt>5)
                    {
                            break;
                    }
                    cnt++;
            }
    
            close(fd);
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    进程从管道读取

    #include 
    #include 
    #include
    #include
    #include
    #include 
    #include
    #include 
    
    
    //有名管道的读取fifo
    int main()
    {
            char buf[30]={0};
            int nread=0;
    
            if(mkfifo("flie",0600==-1) && errno!=EEXIST)
            {
                    printf("mkfifo failed\n");
                    perror("why");
            }
    
            int fd=open("flie",O_RDONLY);
            printf("open success\n");
            while(1)
            {
                    nread=read(fd,buf,30);
                    printf("read %d byte from fifo,context:%s\n",nread,buf);
            }
            close(fd);
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    二、消息队列

    消息队列保存在内核中的消息链表,在发送数据时,会分成一个一个独立的数据单元,也就是消息体(数据块),消息体是用户自定义的数据类型,消息的发送方和接收方要约定好消息体的数据类型,所以每个消息体都是固定大小的存储块,不像管道是无格式的字节流数据。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除,不必遵循先进先出原则,可以跟据数据类型进行读取。

    消息队列生命周期随内核,如果没有释放消息队列或者没有关闭操作系统,消息队列会一直存在,而前面提到的匿名管道的生命周期,是随进程的创建而建立,随进程的结束而销毁。
    但是,鉴于传输上限的限制消息队列不适合应用于大型数据的传输。同时,本质是用户态与内核态之间的信息拷贝,增大了工作量。

    2.1 msg 原型

    // 创建或打开消息队列:成功返回队列ID,失败返回-1
    int msgget(key_t key, int flag);
    // 添加消息:成功返回0,失败返回-1
    int msgsnd(int msqid, const void *ptr, size_t size, int flag);
    // 读取消息:成功返回消息数据的长度,失败返回-1
    int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
    // 控制消息队列:成功返回0,失败返回-1
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.2 msg 实例

    2.2.1 单向通信实例

    发送端

    #include 
    #include 
    #include 
    #include 
    #include 
    
    struct msgbuf
    {
            long mtype;
            char mtext[128];
    };
    
    int main()
    {
            struct msgbuf  sendbuf={888,"This is message from quen"};
    
            //1. int msgget(key_t key, int msgflg);
            int msgid=msgget(0x1234,IPC_CREAT|0777);
    
            if(msgid==-1)
            {
                    printf("creat que failed!\n");
            }
            //2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    
            //3. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
    
            msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    接收端

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    
    struct msgbuf
    {
            long mtype;
            char mtext[128];
    };
    
    int main()
    {
            struct msgbuf  readbuf;
            //1. int msgget(key_t key, int msgflg);
            int msgid=msgget(0x1234,IPC_CREAT|0777);
            if(msgid==-1)
            {
                    printf("creat que failed!\n");
            }
            // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    
            // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
    
            msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);
            printf("read from que:%s\n",readbuf.mtext);
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.2.2 双向通信实例

    先发再读

    #include 
    #include 
    #include 
    #include 
    #include 
    //       int msgget(key_t key, int msgflg);
    // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
     
    //       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    struct msgbuf{
            long mtype;       /* message type, must be > 0 */
            char mtext[128];    /* message data */
    };
     
     
    int main()
    {
            struct msgbuf sendbuf={888,"message from send"};
            struct msgbuf readbuf;
     
            key_t key;
     
            if((key = ftok(".",'z')) < 0){
                    printf("ftok error\n");
            }
            int msgId = msgget(key,IPC_CREAT|0777);
     
            if(msgId == -1){
                    printf("get quen failed\n");
            }
     
            msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
            printf("send over\n");
     
            msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),999,0);
            printf("read from get is:%s\n",readbuf.mtext);
     
            msgctl(msgId,IPC_RMID,NULL);
     
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    先读再发

    #include 
    #include 
    #include 
    #include 
    #include 
    //       int msgget(key_t key, int msgflg);
    // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
     
    //       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    struct msgbuf{
            long mtype;       /* message type, must be > 0 */
            char mtext[128];    /* message data */
    };
     
    int main()
    {
            struct msgbuf readbuf;
            memset(readbuf.mtext, '\0', sizeof(readbuf.mtext));
            struct msgbuf sendbuf={999,"thank for your reach"};
     
            key_t key;
     
            //获取key值
            if((key = ftok(".",'z')) < 0){
                    printf("ftok error\n");
            }
     
            int msgId = msgget(key,IPC_CREAT|0777);
     
            if(msgId == -1){
                    printf("get quen failed\n");
                    perror("why");
            }
     
            msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),888,0);
            printf("read from send is:%s\n",readbuf.mtext);
     
            msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
     
            msgctl(msgId,IPC_RMID,NULL);
     
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    三、共享内存

    消息队列的读取和写入的过程,都会有发生用户态与内核态之间的消息拷贝过程。那共享内存的方式,就很好的解决了这一问题。共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写入的东西,另外一个进程马上就能看到了,不需要拷贝来拷贝去,传来传去,大大提高了进程间通信的速度

    3.1 共享内存的原型

    // 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
    int shmget(key_t key, size_t size, int flag);
    // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
    void *shmat(int shm_id, const void *addr, int flag);
    // 断开与共享内存的连接:成功返回0,失败返回-1
    int shmdt(void *addr); 
    // 控制共享内存的相关信息:成功返回0,失败返回-1
    int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.2 shm 实例

    发送数据

    #include 
    #include 
    #include 
    //共享内存的创建
    
    //int shmget(key_t key, size_t size, int shmflg);
    
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //int shmdt(const void *shmaddr);
    
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //int shmdt(const void *shmaddr);
    
    int main()
    {
            key_t key;
            key=ftok(".",1);
    
            int shmid=shmget(key,1024*4,IPC_CREAT|0777);//创建,内存大小必须得是MB的整数倍
            if(shmid==-1)
            {
                    printf("failed!\n");
                    exit(-1);
            }
    
            char *shmadder=shmat(shmid,0,0);//映射挂载
            printf("shmat ok\n");
            strcpy(shmadder,"message frome send");
    
            sleep(5);//等待五秒
    
            shmdt(shmadder);//卸载
            shmctl(shmid,IPC_RMID,0);//删除空间
    
            printf("quit!!!\n");
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    读取数据

    #include 
    #include 
    #include 
    #include 
    //共享内存的创建
    
    //int shmget(key_t key, size_t size, int shmflg);
    
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //int shmdt(const void *shmaddr);
    
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //int shmdt(const void *shmaddr);
    
    int main()
    {
            key_t key;
            key=ftok(".",1);
    
            int shmid=shmget(key,1024*4,0);//获取
            if(shmid==-1)
            {
                    printf("failed!\n");
                    exit(-1);
            }
    
            char *shmadder=shmat(shmid,0,0);//映射挂载
            printf("shmat ok\n");
            printf("data:%s\n",shmadder);
    
    
            shmdt(shmadder);//卸载
            shmctl(shmid,IPC_RMID,0);//删除空间
    
            printf("quit!!!\n");
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、信号量

    信号量与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥同步,而不是用于存储进程间通信数据。信号量基于操作系统的 P、V原子操作,用于进程间同步,若要在进程间传递数据需要结合共享内存。每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

    4.1 信号量原型

    // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
    int semget(key_t key, int num_sems, int sem_flags);
    // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
    int semop(int semid, struct sembuf semoparray[], size_t numops);  
    // 控制信号量的相关信息
    int semctl(int semid, int sem_num, int cmd, ...);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.2 信号量实例

    #include 
    #include 
    #include 
    #include 
    #include 
     
    //       int semget(key_t key, int nsems, int semflg);
    //       int semctl(int semid, int semnum, int cmd, ...);
    //       int semop(int semid, struct sembuf *sops, size_t nsops);
    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) */
    };
     
    //P操作,拿钥匙
    void PGetKey(int semid)
    {
            struct sembuf sops;
            sops.sem_num = 0;// 信号量组中对应的序号,0~sem_nums-1
            sops.sem_op = -1;// 信号量值在一次操作中的改变量
            sops.sem_flg = SEM_UNDO;// IPC_NOWAIT, SEM_UNDO
     
            semop(semid, &sops, 1);
    }
     
    //V操作,放回钥匙
    void VPutBackKey(int semid)
    {
            struct sembuf sops;
            sops.sem_num = 0;
            sops.sem_op = 1;
            sops.sem_flg = SEM_UNDO;
     
            semop(semid, &sops, 1);
    }
     
    int main()
    {
            key_t key;
            int semid;
            if((key == ftok(".",6)) < 0)
            {
                    printf("ftok error\n");
            }
     
            semid = semget(key , 1,  IPC_CREAT|0666);//创造信号量集合 钥匙,数量为1
     
            union semun sem;
            sem.val = 0;//初始状态为没有钥匙
            semctl(semid, 0, SETVAL, sem);//SETVAL初始化信号量为一个已知的值,这时就需要第四个参数
                         //0表示操作第一把钥匙
            int pid = fork();
     
            if(pid < 0)
            {
                    printf("fork failed\n");
            }else if(pid == 0)//子进程
            {
                    printf("this is child\n");
                    VPutBackKey(semid);//子进程运行结束后把钥匙放回     
            }else//父进程
            {
                    PGetKey(semid);//等子进程将钥匙放回后才会进行操作,保证子进程先执行(最开始无锁状态,父进程拿不到锁,进程堵塞)
                    printf("this is father\n");
                    VPutBackKey(semid);
                    semctl(semid, 0, IPC_RMID);//销毁钥匙
            }
     
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    五、信号

    信号signal属于软中断,属于异步通讯。终端用户输入了 ctrl+c 来中断程序,会通过信号机制(SIGINT)停止一个程序。
    信号处理的三种方法分别是:忽略捕捉
    默认动作

    5.1 signal

    5.1.1信号signal原型

         //接收函数,第二个参数指向信号处理函数
        sighandler_t signal(int signum, sighandler_t handler);
    
        //发送函数
         int kill(pid_t pid, int sig);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.1.2信号sig实例

    接收

    #include 
    #include 
     
    //       typedef void (*sighandler_t)(int);
     
    //       sighandler_t signal(int signum, sighandler_t handler);
    /*接受到信号后,让信号处理该函数*/
    void handler(int signum)
    {
            printf("signum = %d\n",signum);
     
            switch(signum){
                    case 2:
                            printf("SIGINT\n");
                            break;
                    case 9:
                            printf("SIGKILL\n");
                            break;
                    case 10:
                            printf("SIGUSR1\n");
                            break;
            }
    }
     
    int main()
    {
            signal(SIGINT,handler);
            signal(SIGKILL,handler);
            signal(SIGUSR1,handler);
     
            while(1);
     
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    发送

    #include 
    #include 
    #include 
    #include 
     
    //       int kill(pid_t pid, int sig);
     
    int main(int argc,char **argv)
    {
            int signum;
            int pid;
            char cmd[128]={0};
     
            signum = atoi(argv[1]);//将字符型转为整型 //消息号       
            pid = atoi(argv[2]);//进程号
     
            kill(pid,signum);//进程号,信号编号
            
     //		sprintf(cmd,"kill -%d%d",signum,pid);       
     //     system(cmd);
            printf("signum = %d,pid = %d\n",signum,pid);
     
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.2 sigaction

    5.2.1 sigaction 原型

    #include 
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
     
    struct sigaction {
       void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
       void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
       sigset_t   sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
       int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
     };
    //回调函数句柄sa_handler、sa_sigaction只能任选其一
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.2.2 sigaction 实例

    接收端

    #include 
    #include 
    #include 
    #include 
    #include 
    //       int sigaction(int signum, const struct sigaction *act,
    //                     struct sigaction *oldact);
    
    void handler(int signum ,siginfo_t *info,void *context)
    {
            printf("get signum%d\n",signum);
            if(context!=NULL)
            {
                    printf("get data=%d\n",info->si_int);
                    printf("from:%d\n",info->si_pid);
            }
    }
    
    
    int main()
    {
    
            struct sigaction act;
            printf("pid:%d\n",getpid());
    
            act.sa_sigaction = handler;
            act.sa_flags = SA_SIGINFO;//be able to get message
    
            sigaction(SIGUSR1,&act,NULL);//注册信号
            while(1);
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    发送端

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //  int sigqueue(pid_t pid, int sig, const union sigval value);
    
    
    int main(int argc,char **argv)
    {
            int signum;
    
            int pid;
    
            union sigval value;
            value.sival_int=100;
    
    
            signum = atoi(argv[1]);
            pid = atoi(argv[2]);
            sigqueue(pid,signum,value);
            printf("send successful\n");
            printf("pid=%d,done\n",getpid());
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    六、Socket

  • 相关阅读:
    【★★★★★ 第2章 线性表总结笔记 2022 9.12】
    热门Java开发工具IDEA入门指南——创建新的Java应用程序(上)
    流程控制语句 流程控制开关
    【基本算法题-2022.7.27】5. 递归实现指数型枚举
    VScode-settings配置文件-插件目录-个人常用配置导出
    Java测试复盘(五)
    大数据学习3.1 环境准备(SSH服务配置)
    Jenkins - 打造强大的前端自动化工作流
    k8s~envoy上添加wasm插件
    TC3-Vision应用笔记
  • 原文地址:https://blog.csdn.net/ONERYJHHH/article/details/126534712