• 进程间通信总结


    进程:一个程序的一次执行过程,是系统调度的单位,资源管理和程序执行的最小单位,线程是调度的最小单位。

    程序:是静态的,是一个可执行文件,保存在硬盘上的一些指令的有序集合。

    进程的执行过程:指令和数据是存放在硬盘上的,这是前提。首先运行程序---》系统会将硬盘上的指令和数据加载到内存中,(如果cpu每次都去访问硬盘,速度是非常慢的,而直接访问内存,这将非常的快)---cpu则访问内存执行代码;

    进程包含三个段:代码段(存放程序代码)。

    数据段:存放全局变量,malloc动态分配的地址空间,以及常数。

    堆栈段:存放局部变量,子程序返回地址,子程序参数。

    内存分为以下5个部分:(一个进程以创建,系统就会自动为该进程分配4G的内存,3G用户空间,1G内核空间(通过系统调用来访问内核空间))

    栈区:由编译器自动分配和释放,存放函数的参数,局部变量等。

    堆区:由程序员分配释放,若没有释放,程序结束时有OS来释放。

    全局区(static):存放全局变量和静态变量,初始化了的放在一边,未初始化的放一边。

    程序代码区:存放程序的二进制代码。

    文字常量区:常量字符串,程序结束后由OS释放。

    每个进程都有一个独有的pid

    获取pid :函数有:getpid(); getppid();

    命令有:ps –ef          ps -aux

    Linux进程的7个状态:1.执行态,2.就绪态,3.阻塞态(浅睡眠,深睡眠),4.暂停态,5.死亡态(僵尸态(struct task_strucct未被清除),死亡态)。

    进程的操作apifork, wait, waitpid, signal, pause, exec, kill ,exit

    进程创建:最后由父进程回收子进程结构体。

    #include "myhead.h"//创建多个子进程,他们属于同一父进程。

    int main(int argc, char **argv)

    {

    int n;

    n = atoi(argv[1]);

    pid_t a;

    int i;

    for(i=0; i<n; i++){

    a = fork();

    if(a > 0) // parent//父进程中返回所创建的子进程的pid

    continue;

    if(a == 0) // child可以通过getpid()获取该子进程的pid

    break;

    }

    if(a > 0){

    for(i=0; i<n; i++)

    wait(NULL);

    }

    printf("pid: %d, ppid: %d ",

    getpid(), getppid());

    exit(0);

    }//

    Exec():fork创建的子进程几乎拷贝了父进程的全部的内容。Exec用于更新子进程中的代码,数据段,栈段,使该进程只含有子进程它自身的代码。

    代码示例:

    exec函数

    execl , execv, execle, execvp, execve.

    int main()

    {

    fork()

    if( ==0)

    {

    execl("可执行文件路径", 可执行文件名,"111","222","333"NULL);//需在子进程中执行。更多示例请参考代码文件夹。

    }

    }

    Linuc守护进程:8个步骤

    #include <unistd.h>

    #include <sys/stat.h>

    #include <syslog.h>

    #include <sys/types.h>

    #include <stdlib.h>

    #include <errno.h>

    #include <sys/file.h>

    #include <stdio.h>

    #include <sys/resource.h>

    #include <signal.h>

    int main(void)

    {

    pid_t pid;

    int max_fd, i;

    /***************************************

    1. generate a child process, to ensure

       successfully calling setsid()

    ****************************************/

    pid = fork();

    if(pid > 0)

    exit(0);

    /*********************************************

    2. ignore the signal SIGHUP, prevent the

       process from being killed by the shutdown

       of the present controlling termination

    **********************************************/

    signal(SIGHUP, SIG_IGN);

    /******************************************************

    3. call setsid(), let the first child process running

       in a new session without a controlling termination

    *******************************************************/

    setsid();

    /*************************************************

    4. generate the second child process, to ensure

       that the daemon cannot open a terminal file

       to become its controlling termination

    **************************************************/

    pid = fork();

    if(pid > 0)

    exit(0);

    /*********************************************************

    5. detach the daemon from its original process group, to

       prevent any signal sent to it from being delivered

    **********************************************************/

    setpgrp();

    /*************************************************

    6. close any file descriptor to release resource

    **************************************************/

    max_fd = sysconf(_SC_OPEN_MAX);

    for(i=0; i<max_fd; i++)

    close(i);

    /******************************************

    7. clear the file permission mask to zero

    *******************************************/

    umask(0);

    /****************************************

    8. change the process's work directory,

       to ensure it won't be uninstalled

    *****************************************/

    chdir("/");

    // Congratulations! Now, this process is a DAEMON!

    pause();

    return 0;

    }

    进程间通信:为何要有进程通信:两个进程同时进行时,这两个进程间需要有些交互。比如MP3播放器,解码是一个进程,同时另一个用户操作也是一个进程,解码之后就需要将解码的数据交给用户操作的进程来执行。

    无名管道:

    注意一下几点: 只有能用于具有亲缘关系的进程之间的通信。

    半双工通信方式,具有固定的读端和写端fd[0]读,fd[1]写。

    它是一种特殊的文件,使用read,write来读写。

    基本操作:

    创建第三方(无名管道):pipe(fd);

    将第三方与进程形成映射:fd

    读写数据readwrite;

    撤销映射:closefd[0];

    删除第三方;

    int main()

    {

    int fd[2]

    pipe(fd);//创建管道,必须在进程之前,不然父进程就无法,将其拷贝给子进程。

    r=fork();//创建进程

    if(r==0)//子进程从fd中读出数据

    {

    close(fd[1]);//使用fd[0]时,记得关闭子进程的fd[1].

    read(fd[0],buf,4);

    printf();

    close(fd[0]);

    }

    else if(r>0)//父进程向fd[1]中写入数据

    {

    sleep(1);

    close(fd[0]);

    write(fd[1],"abef",);

    close(fd[1]);

    }

    }

    关于读写需注意几点:1.当管道中无数据时,读操作会阻塞

    2.向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。

    3.如果读端已关闭  ,写数据  ,进程退出

    4.如果写端已关闭,读数据  --->0

    有名管道: 可以使互不相关的两个进程间进行通信。

    遵循先进先出的规则。

    通过文件io来操作。

    不支持如lseek();

    1. 创建有名管道mkfifo
    2. 映射有名管道open(fd)
    3. 读写:readwrite
    4. 撤销映射:close(fd);
    5. 删除有名管道文件;

    int main()

    {

    mkfifo("./fifo",O_CREAT|0666)//创建一个有名管道,是一个管道文件p

    int fd=open("./fifo",O_WRONLY) //打开管道得到一个文件描述符fd

    wrtie(fd,“” ,)//向文件描述符中写入数据

    close(fd); // 关闭文件描述符

    }

    p2.c

    int main()

    {

    int fd=open("./fifo",O_RDONLY);//打开同一个管道文件,得到一个文件描述符

    char buf[12];//

    int r=read(fd,buf,11);//读取文件描述符中的数据

    buf[r]='';对字符串的操作,这不很关键,没有这步,易出现乱码。

    printf("%s",buf);

    close(fd);

    }

    关于读写需要注意的几点容易出错,或者打不开管道:

    ① open(const char *path, O_RDONLY );

    在这种情况下,open 调用将阻塞,除非有一个 进程以写方式打开同一个FIFO,否则它不会返回。

    open(const char *path, O_RDONLY|O_NONBLOCK )

    即使没有其它进程以写方式打开FIFO,这open调 用也将成功并马上返回

    open(const char *path, O_WRONLY)

    open调用将阻塞,直到有一个进程以读方式打开同一个 FIFO为止。

    open(const char *path, O_WRONLY|O_NONBLOCK)

    open调用总是立刻返回,便如果没有进程以读方式打开FIFO文件, open调用将返回一个错误(-1)并且FIFO也不会被打开。

    比较好:

    #include<stdio.h>

    #include<unistd.h>

    #include<sys/types.h>

    #include<stdlib.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include<string.h>

    int main()

    {

    if(mkfifo("/home/gec/fifo",O_CREAT|0666)==-1)

    perror("mkfifo");

    char str[10];

    int fd=open("/home/gec/fifo",O_WRONLY);

    printf("start ");

    if(fd==-1)

    {

    printf("open error: ");

    }

    while(1)

    {

    pid_t  ret= fork();

    if(ret<0)

    {

    printf("fork1 error:");

    }

    if(ret==0)

    {

    sprintf(str,"%d",getpid());//spriintf的使用;

    write(fd,str,strlen(str));//strlen()的使用

    printf("%s ",str);

    sleep(1);

    printf("write succes ");

    exit(0);

    }

    wait(NULL);

    }

    }

    信号:

    在软件层次上对中断机制的一种模拟,是一种异步通信模式。

    可以直接进行内核进程和用户进程之间的交互。

    信号发送,信号阻塞(OS),信号的响应。

    Raise(int sig)//向自己发送信号

    Alarm(5)//5s后向自己发送SIGALRM信号,接收到这个信号后,进程遇到pause(),暂停5秒后继续向下执行。(一个进程最多有一个闹钟,如果有多个闹钟,以后一个为准)

    //#include "myhead.h"

    #include <unistd.h>

    #include<signal.h>

    #include<sys/types.h>

    #include <sys/wait.h>

    #include<stdio.h>

    #include <stdlib.h>

    pid_t a, b;//灵活使用全局变量

    void catch_sig(int sig)

    {

    kill(a, SIGUSR1);/向子进程发送信号SIGUSR1

    kill(b, SIGUSR2);

    printf("%d,%d ",a,b);

    }

    void f(int sig)

    {

    printf("child 1 catch the signal: %d ", sig);

    }

    void g(int sig)

    {

    printf("child 2 catch the signal: %d ", sig);

    }

    int main(void)

    {

    signal(SIGINT, catch_sig);//父进程中注册信号SIGINT

    a = fork();

    if(a > 0){

    b = fork();

    if(b > 0){ // parent

    pause();

    wait(NULL);//等待子进程退出,父进程继续执行

    wait(NULL);

    printf("parent exit! ");

    exit(0);

    }

    else if(b == 0){ // child 2

    printf("pid=%d ",getpid());

    signal(SIGUSR2, g);//注册信号SIGUSR2

    pause();

    exit(0);

    }

    }

    else if(a == 0){ // child 1

    printf("pid=%d ",getpid());

    signal(SIGUSR1, f);

    pause();//暂停进程信号

    exit(0);

    }

    }

    Alarm:

    #include <unistd.h>

    #include<signal.h>

    #include<sys/types.h>

    #include <sys/wait.h>

    #include<stdio.h>

    #include <stdlib.h>

    int main()

    {

    int ret;

    ret =alarm(5);

    printf("wakenff  ");

    sleep(1);

    printf("after sleep1, %d ",alarm(3));

    pause();

    printf("waken  ");

    return 0;

    }

    Forkkill:

    #include <unistd.h>

    #include<signal.h>

    #include<sys/types.h>

    #include <sys/wait.h>

    #include<stdio.h>

    #include <stdlib.h>

    int main()

    {

    pid_t pid;

    int ret;

    if((pid=fork())<0)

    {

    perror("fork");

    exit(1);

    }

    if(pid==0)

    {

    printf("chiled process  ");

    raise(SIGSTOP);

    printf("chiled process exit  ");

    exit(0);

    }

    else

    { sleep(1);

    printf("pid =%d  ",pid);

    if((waitpid(pid,NULL,WNOHANG))==0)

    {

    kill(pid,SIGKILL);

    printf("kill %d ",pid);

    }

    }

    }

    Signaltest:

    #include <unistd.h>

    #include<signal.h>

    #include<sys/types.h>

    #include <sys/wait.h>

    #include<stdio.h>

    #include <stdlib.h>

    void my_func(int sign)

    {

    if(sign==SIGINT)

    printf("i have got sigint ");

    else if(sign==SIGQUIT)

    printf("i have got sigquit ");

    }

    int main()

    {

    printf("waitting for signal sigint or sigquit ");

    signal(SIGINT,my_func);

    signal(SIGQUIT,my_func);

    pause();

    printf("exit ");

    exit(0);

    }

    拓展:

    #include<stdio.h>

    #include<signal.h>

    #include<stdlib.h>

    int output(sigset_t set)

    {

    printf("set.val[0]=%x ",set.__val[0]);

    }

    void handler(int sig)

    {

    int i;

    sigset_t sysset;

    printf("  nin hadler sig=%d ",sig);

    sigprocmask(SIG_SETMASK,NULL,&sysset);

    output(sysset);

    printf("return ");

    }

    struct sigaction {

                   void     (*sa_handler)(int);

                   void     (*sa_sigaction)(int, siginfo_t *, void *);

                   sigset_t   sa_mask;

                   int        sa_flags;

                   void     (*sa_restorer)(void);

               };

    sa_flags=SA_SIGINFO,时,使用第二个函数原型。

    int main()

    {

    struct sigaction act;

    sigset_t set,sysset,newset;

    sigemptyset(&set);

    sigemptyset(&newset);//清空结构体阻塞信号集

    sigaddset(&set,SIGUSR1);//将信号加入到结构体阻塞信号集sysset

    sigaddset(&newset,SIGUSR2);

    printf(" add sigusr1,the value of set");

    output(set);//200

    printf(" add sigusr2,the value of newset");

    output(newset);//800

    printf(" after set proc block set ,and then read to sysset ");

    //将结构体阻塞信号集(在命令程序运行期间阻塞)set加入到进程阻塞信号集中

    sigprocmask(SIG_SETMASK,&set,NULL);

    //将进程阻塞信号集加入到结构体阻塞信号集sysset

    sigprocmask(SIG_SETMASK,NULL,&sysset);

    printf("system mask is: ");

    output(sysset);//2a00>200+800

    printf("install sigalrm,and the act.sammask is newset(siguser2) ");

    act.sa_handler=handler;

    act.sa_flags=0;//表示使用结构体中的前一个函数原型

    act.sa_mask=newset;

    sigaction(SIGALRM,&act,NULL);//运行之后该信号会自动加入到进程阻塞信号集(始终都阻塞)中。

    pause();

    printf("after exec isr ");

    sigemptyset(&sysset);

    sigprocmask(SIG_SETMASK,NULL,&sysset);

    output(sysset);//200

    }

    信号量集:systemV(semphore)

    1. ftok:得到一个唯一的semid;
    2. 创建一个信号量集semget,返回一个唯一的semid;
    3. 初始化信号量集semctl(SETVAL)
    4. 判断信号量的值semctl(GETVAL);
    5. 操作信号量的值semop(),主要是设置一个结构体struct sembuf

    {

    Short sem_num;信号量编号

    Short sem_op;对应的操作+1v),-1(p)

    Short sem_flg;SEM_UNDO

    }

    6.读写数据

    7.删除信号量集semctl(IPC_RMID);

    Share memory(共享内存)

    最为高效的通信方式,进程可以直接读写数据,而不需要拷贝。

    内核专门留出了一块内存,可以将其映射到用户空间某段内存。用户可以直接操作这段内存来间接操作内核内存,实现通信。

    多个进程共享这段内存,需要同步机制(信号量)和互斥所.

    共享内存使用步骤:

    1. ftok:得到一个唯一的key
    2. 创建打开共享内存shmget,返回一个shmid;
    3. 映射一段共享内存shmat,返回内存地址的指针void *
    4. 读写操作
    5. 撤销共享内存映射shmdt
    6. 删除共享内存(一般有接收进程来实现)shmctl()

    将一个结构体的数据写入是相同的操作:将两个进程的的共享内存的指针类型声明为该结构体类型即可,然后通过这个指针来获取数据,如果有多个结构体,指针需向后移。

    SystemV.c(读取数据)

    #include<stdio.h>

    #include<stdlib.h>

    #include<unistd.h>

    #include<sys/types.h>

    #include<sys/shm.h>

    #include<sys/sem.h>

    #include<sys/ipc.h>

    int main()

    {

    key_t key = ftok("./",100);

    if(key==-1)

    perror("shmftok() error!/n");

    int shmid = shmget(key,1024,IPC_CREAT|0666);

    if(shmid==-1)

    perror("shmget() error!/n");

    char *p = shmat(shmid,NULL,0);

    key_t semkey = ftok("./",102);

    if(semkey ==-1)

    perror("semftok() error! ");

    int semid = semget(semkey,2,IPC_CREAT|0666);

    if(semid == -1)

    perror("semget() error! ");

    // semctl(semid,0,SETVAL,1);

    // if(semctl(semid,1,SETVAL,0)==-1)

    // perror("semctl() error! ");

    struct sembuf sembv;

    sembv.sem_num = 0;

    sembv.sem_op = 1;

    sembv.sem_flg = SEM_UNDO;

    struct sembuf sembp;

    sembp.sem_num = 1;

    sembp.sem_op = -1;

    sembp.sem_flg = SEM_UNDO;

    while(1)

    {

    if(semop(semid,&sembp,1)==-1)

    // printf("%d ",semctl(semid,0,GETVAL));

    perror("semop() error! ");

    if(semctl(semid,0,GETVAL)==0)

    {

    printf("%s ",p);

    semop(semid,&sembv,1);

    }

    else

    {

    perror("read error ");

    }

    }

    }

    Systemv1.c(写入数据)

    #include<stdio.h>

    #include<stdlib.h>

    #include<unistd.h>

    #include<sys/types.h>

    #include<sys/shm.h>

    #include<sys/sem.h>

    #include<sys/ipc.h>

    int main()

    {

    key_t key = ftok("./",100);

    if(key==-1)

    perror("shmftok() error!/n");

    int shmid = shmget(key,1024,IPC_CREAT|0666);

    if(shmid==-1)

    perror("shmget() error!/n");

    char *p = shmat(shmid,NULL,0);

    key_t semkey = ftok("./",102);

    if(semkey ==-1)

    perror("semftok() error! ");

    int semid = semget(semkey,2,IPC_CREAT|0666);//创建信号量集,返回semid

    if(semid == -1)

    perror("semget() error! ");

    semctl(semid,0,SETVAL,1);

    if(semctl(semid,1,SETVAL,0)==-1)

    perror("semctl() error! ");

    struct sembuf sembv;

    sembv.sem_num = 1;

    sembv.sem_op = 1;

    sembv.sem_flg = SEM_UNDO;

    struct sembuf sembp;

    sembp.sem_num = 0;

    sembp.sem_op = -1;

    sembp.sem_flg = SEM_UNDO;

    while(1)

    {

    // printf("%d ",semctl(semid,0,GETVAL));

    if(semop(semid,&sembp,1)==-1)

    //printf("%d ",semctl(semid,0,GETVAL));

    perror("semop() error! ");

    if(semctl(semid,0,GETVAL)==0)

    {

    scanf("%s",p);

    semop(semid,&sembv,1);

    }

    else

    {

    perror("write error ");

    }

    }

    }

    消息队列:

    1. 通过ftok得到一个唯一的key
    2. 创建消息队列msgget(),返回一个唯一的msgid;
    3. 发送消息,接收消息;消息结构体典型的类型:struct msgbuf
      1. {
      2. long mtype;
      3. char mtext[];
      4. }
      5. 删除消息队列:msgctl(IPC_RMID)

    message.c(先发后收)两个进程之间进行通信

    #include<stdlib.h>

    #include<stdio.h>

    #include<sys/msg.h>

    #include<sys/types.h>

    #include<sys/ipc.h>

    #include<string.h>

    #include<sys/sem.h>

    struct msgbuf

    {

    long mtype;

    char mtext[12];

    };

    int main()

    {

    key_t key = ftok("./",20);

    if(key==-1)

    perror("ftok() error ");

    int msgid = msgget(key,IPC_CREAT|0666);

    if(msgid==-1)

    perror("msgget() error! ");

     

    struct msgbuf send1,rcv1;

    send1.mtype = 200;

    strcpy(send1.mtext,"hello!");

     

     

    key_t semkey = ftok("./",21);

    int semid = semget(semkey,4,IPC_CREAT|0666);

    if(semid ==-1)

    perror("semget() error! ");

     

    semctl(semid,0,SETVAL,1);

    semctl(semid,1,SETVAL,0);

    semctl(semid,2,SETVAL,0);

    semctl(semid,3,SETVAL,0);

    struct sembuf semb0,semb1,semb2,semb3;

    semb0.sem_num = 0;

    semb0.sem_op = -1;

    semb0.sem_flg = SEM_UNDO;

     

    semb1.sem_num = 1;

    // semp1.sem_op = 1;

    semb1.sem_flg = SEM_UNDO;

     

    semb2.sem_num = 2;

    // semp.sem_op = -1;

    semb2.sem_flg = SEM_UNDO;

     

    semb3.sem_num = 3;

    // semp.sem_op = -1;

    semb3.sem_flg = SEM_UNDO;

     

     

    while(1)

    {

    semb0.sem_op = -1;

    if(semop(semid,&semb0,1)==-1)

    perror("semop() error! ");

    if(semctl(semid,0,GETVAL)==0)

    {

    char messag[30];

    printf("please input:");

    scanf("%s",messag);

    strcpy(send1.mtext,messag);

    msgsnd(msgid,&send1,strlen(send1.mtext)+1,0);

    semb1.sem_op = 1;

    semop(semid,&semb1,1);

    }

    semb3.sem_op = -1;

    semop(semid,&semb3,1);

    if(semctl(semid,3,GETVAL)==0)

    {

    msgrcv(msgid,&rcv1,sizeof(rcv1),100,0);

    printf("accept:");

    printf("%s ",rcv1.mtext);

    semb0.sem_op = 1;

    semop(semid,&semb0,1);

    }

    // else

    // {

    // perror("msg1 error! ");

    // }

    }

     

    }

    message2.c(先收后发)两个进程之间进行通信

    #include<stdlib.h>

    #include<stdio.h>

    #include<sys/msg.h>

    #include<sys/types.h>

    #include<sys/ipc.h>

    #include<string.h>

    #include<sys/sem.h>

    struct msgbuf

    {

    long mtype;

    char mtext[12];

    };

    int main()

    {

    key_t key = ftok("./",20);

    if(key==-1)

    perror("ftok() error ");

    int msgid = msgget(key,IPC_CREAT|0666);

    if(msgid==-1)

    perror("msgget() error! ");

     

    struct msgbuf send1,rcv1;

    send1.mtype = 100;

    // strcpy(send1.mtext,"hello!");

     

     

    key_t semkey = ftok("./",21);

    int semid = semget(semkey,4,IPC_CREAT|0666);

    if(semid ==-1)

    perror("semget() error! ");

     

    // semctl(semid,0,SETVAL,1);

    // semctl(semid,1,SETVAL,0);

     

    struct sembuf semb0,semb1,semb2,semb3;

    semb0.sem_num = 0;

    // semb0.sem_op = -1;

    semb0.sem_flg = SEM_UNDO;

     

    semb1.sem_num = 1;

    // semb1.sem_op = 1;

    semb1.sem_flg = SEM_UNDO;

     

    semb2.sem_num = 2;

    // semb1.sem_op = 1;

    semb2.sem_flg = SEM_UNDO;

     

    semb3.sem_num = 3;

    // semb1.sem_op = 1;

    semb3.sem_flg = SEM_UNDO;

     

    while(1)

    {

    semb1.sem_op=-1;

    // semop(semid,&semb1,1);

    if(semop(semid,&semb1,1)==-1)

    perror("semop() error! ");

    if(semctl(semid,1,GETVAL)==0)

    {

    msgrcv(msgid,&rcv1,sizeof(rcv1),200,0);

    printf("accept:");

    printf("%s ",rcv1.mtext);

    semb2.sem_op = 1;

    semop(semid,&semb2,1);

    }

    semb2.sem_op = -1;

    semop(semid,&semb2,1);

    if(semctl(semid,2,GETVAL)==0)

    {

    char messag[30];

    printf("please input:");

    scanf("%s",messag);

    strcpy(send1.mtext,messag);

    msgsnd(msgid,&send1,strlen(send1.mtext)+1,0);

    semb3.sem_op = 1;

    semop(semid,&semb3,1);

    }

    // else

    // {

    // perror("msg2 error! ");

    // }

    }

     

    }

  • 相关阅读:
    4270. 【NOIP2015模拟10.27】魔道研究
    4269. 【NOIP2015模拟10.27】挑竹签
    NOIP2015模拟10.28B组
    JZOI5257. 小X的佛光
    4260. 最大子矩阵 (Standard IO)
    1010. 【CQOI2009】叶子的颜色
    【NOIP2015模拟10.22】最小代价
    JZOI 距离 (Standard IO) 题解
    统计和 luogu P2068 树状数组和线段树练手
    2020.7.15模拟赛
  • 原文地址:https://www.cnblogs.com/defen/p/5251685.html
Copyright © 2020-2023  润新知