• Unix IPC之基于共享内存的计数器


    目的

    本文主要实现一个基于共享内存的计数器,通过父子进程对其访问。

    本文程序需基于<<Unix网络编程-卷2>>的环境才能运行。程序中大写开头的函数为其小写同名函数的包裹函数,增加了错误处理信息。

    1 函数介绍
    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
    #include <sys/mman>
     
    /**
     * Map addresses starting near ADDR and extending for LEN bytes.
     * from OFFSET into the file FD describes according to PROT and FLAGS.
     * If ADDR is nonzero, it is the desired mapping address.
     * If the MAP_FIXED bit is set in FLAGS, the mapping will be at ADDR exactly (which must be
     * page-aligned); otherwise the system chooses a convenient nearby address.
     * The return value is the actual mapping address chosen or MAP_FAILED
     * for errors (in which case `errno' is set).  A successful `mmap' call
     * deallocates any previous mapping for the affected region.
     */
    void *mmap (void *__addr, size_t __len, int __prot,
                int __flags, int __fd, __off_t __offset);
     
    /* Deallocate any mapping for the region starting at ADDR and extending LEN
       bytes.  Returns 0 if successful, -1 for errors (and sets errno).  */
    extern int munmap (void *__addr, size_t __len) __THROW;
     
    /* Synchronize the region starting at ADDR and extending LEN bytes with the
       file it maps.  Filesystem operations on a file being mapped are
       unpredictable before this is done.  Flags are from the MS_* set.
     
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern int msync (void *__addr, size_t __len, int __flags);
    2 计数器——非共享内存

    默认情况下,通过fork派生的子进程并不与其父进程共享内存区。

    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
    #include    "unpipc.h"
     
    #define SEM_NAME    "mysem"
    int     count = 0; // 计数器
     
    int main(int argc, char **argv)
    {
        int     i, nloop;
        sem_t   *mutex;
     
        if (argc != 2)
            err_quit("usage: incr1 <#loops>");
        nloop = atoi(argv[1]);
     
        /* 4create, initialize, and unlink semaphore */
        mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1);
        /**
         * sem_unlink() 
         * removes the named semaphore referred to by name. 
         * The semaphore name is removed immediately. 
         * The semaphore is destroyed once all other processes that have the semaphore open close it.
         */
        Sem_unlink(Px_ipc_name(SEM_NAME));
     
        // 非缓冲模式,防止两个线程的输出交叉
        // 父子线程共同访问一个信号量
        // 由于mutex是2值信号量,相当于同步父子线程
        // 所以实际上这里并不会发生交叉
        setbuf(stdout, NULL);   /* stdout is unbuffered */
        if (Fork() == 0)        /* child */
        {
            for (i = 0; i < nloop; i++)
            {
                Sem_wait(mutex);
                printf("child: %d ", count++);
                Sem_post(mutex);
            }
            exit(0);
        }
     
        /* 4parent */
        for (i = 0; i < nloop; i++)
        {
            Sem_wait(mutex);
            printf("parent: %d ", count++);
            Sem_post(mutex);
        }
        exit(0);
    }

    由于子进程为父进程的拷贝,所以子进程自己有一个count的副本,所以父子进程操作自己的count。这里使用一个有名信号量来同步父子进程。

    3 计数器——共享内存

    程序说明:

    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
    #include    "unpipc.h"
     
    #define SEM_NAME    "mysem" // 有名信号量用于同步父子进程(加锁)
     
    int main(int argc, char **argv)
    {
        int     fd, i, nloop, zero = 0;
        int     *ptr; // 访问共享内存的指针
        sem_t   *mutex;
     
        if (argc != 3)
            err_quit("usage: incr2 <pathname> <#loops>");
        nloop = atoi(argv[2]);
     
        /* 4open file, initialize to 0, map into memory */
        fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
        Write(fd, &zero, sizeof(int)); // 向文件中写入一个int型的0
        // ptr返回共享内存起始位置
        ptr = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 映射一个int型大小的共享内存
        Close(fd);
     
        /* 4create, initialize, and unlink semaphore */
        mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1); // 信号量
        Sem_unlink(Px_ipc_name(SEM_NAME));
     
        setbuf(stdout, NULL);   /* stdout is unbuffered */
        if (Fork() == 0)        /* child */
        {
            for (i = 0; i < nloop; i++)
            {
                Sem_wait(mutex);
                printf("child: %d ", (*ptr)++);
                Sem_post(mutex);
            }
            exit(0);
        }
     
        /* 4parent */
        for (i = 0; i < nloop; i++)
        {
            Sem_wait(mutex);
            printf("parent: %d ", (*ptr)++);
            Sem_post(mutex);
        }
        exit(0);
    }

    运行结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    [dell@localhost shm]$ ./incr2 ./count.data 100
    parent: 0
    parent: 1
    parent: 2
    parent: 3
    ....
    child: 197
    child: 198
    child: 199
    [dell@localhost shm]$ ll count.data
    -rw-r--r--. 1 dell dell 4 8月  18 14:28 count.data
    [dell@localhost shm]$ file count.data
    count.data: data
    [dell@localhost shm]$ hexdump -d count.data
    0000000   00200   00000                                               
    0000004
    [dell@localhost shm]$

    注意:这里的count.data文件类型data类型,需用od、xxd、hexdump等命令才能查看。

    4 计数器——共享内存(2)

    上面3中的信号量为有名信号量,其具体实现由Posix决定,但是至少是内核持续性的。这里将其改为基于内存的信号量,并将其放置在共享内存中。

    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
    #include    "unpipc.h"
     
    struct shared
    {
        sem_t mutex;      /* the mutex: a Posix memory-based semaphore */ // 匿名信号量
        int   count;      /* and the counter */
    } shared;
     
    int main(int argc, char **argv)
    {
        int     fd, i, nloop;
        struct shared   *ptr;
     
        if (argc != 3)
            err_quit("usage: incr3 <pathname> <#loops>");
        nloop = atoi(argv[2]);
     
        /* 4open file, initialize to 0, map into memory */
        fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
        Write(fd, &shared, sizeof(struct shared));
        ptr = Mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
                   MAP_SHARED, fd, 0);
        Close(fd);
     
        /* 4initialize semaphore that is shared between processes */
        Sem_init(&ptr->mutex, 1, 1); // 初始化匿名信号量,设置为进程共享,初始值为1
     
        setbuf(stdout, NULL);   /* stdout is unbuffered */
        if (Fork() == 0)        /* child */
        {
            for (i = 0; i < nloop; i++)
            {
                Sem_wait(&ptr->mutex);
                printf("child: %d ", ptr->count++);
                Sem_post(&ptr->mutex);
            }
            exit(0);
        }
     
        /* 4parent */
        for (i = 0; i < nloop; i++)
        {
            Sem_wait(&ptr->mutex);
            printf("parent: %d ", ptr->count++);
            Sem_post(&ptr->mutex);
        }
        exit(0);
    }


    5 计数器——共享内存(无亲缘关系进程)

     服务器端:创建有名信号量及共享内存

    #include    "unpipc.h"
    
    struct shmstruct    /* struct stored in shared memory */
    {
        int   count;
    };
    sem_t   *mutex;     /* pointer to named semaphore */ // 有名信号量
    
    int
    main(int argc, char **argv)
    {
        int     fd;
        struct shmstruct    *ptr;
    
        if (argc != 3)
            err_quit("usage: server1 <shmname> <semname>");
    
        shm_unlink(Px_ipc_name(argv[1]));       /* OK if this fails */
        /* 4create shm, set its size, map it, close descriptor */
        fd = Shm_open(Px_ipc_name(argv[1]), O_RDWR | O_CREAT | O_EXCL, FILE_MODE);
        Ftruncate(fd, sizeof(struct shmstruct)); // 设置共享内存大小
        ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE,
                   MAP_SHARED, fd, 0);
        Close(fd);
    
        sem_unlink(Px_ipc_name(argv[2]));       /* OK if this fails */
        mutex = Sem_open(Px_ipc_name(argv[2]), O_CREAT | O_EXCL, FILE_MODE, 1);
        Sem_close(mutex);
    
        exit(0);
    }
    View Code

    客户端:使用有名信号量;映射共享内存空间,并给共享内存空间内的计数器+1

    #include    "unpipc.h"
    
    struct shmstruct    /* struct stored in shared memory */
    {
        int   count;
    };
    sem_t   *mutex;     /* pointer to named semaphore */
    
    int
    main(int argc, char **argv)
    {
        int     fd, i, nloop;
        pid_t   pid;
        struct shmstruct    *ptr; // 注意这里的指针类型
    
        if (argc != 4)
            err_quit("usage: client1 <shmname> <semname> <#loops>");
        nloop = atoi(argv[3]);
    
        fd = Shm_open(Px_ipc_name(argv[1]), O_RDWR, FILE_MODE);
        ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE,
                   MAP_SHARED, fd, 0);
        Close(fd);
    
        mutex = Sem_open(Px_ipc_name(argv[2]), 0);
    
        pid = getpid();
        for (i = 0; i < nloop; i++)
        {
            Sem_wait(mutex);
            printf("pid %ld: %d
    ", (long) pid, ptr->count++);
            Sem_post(mutex);
        }
        exit(0);
    }
    View Code


    说明:服务器运行一个实例;客户端运行多个实例,实例之间互斥对计数器进行+1操作;





  • 相关阅读:
    vmware 更换网络后不能上网
    IDEA “Cannot resolve symbol” 解决办法
    SpringBoot MyBatis druid数据库连接池
    解决分页插件ClassNotFoundException: org.springframework.boot.bind.RelaxedPropertyResolver
    纯CSS绘制不同角度的三角形
    原生js实现移动端点击、长按、左滑、右滑、上滑、下滑等事件模拟
    移动端下拉滚动刷新
    使用锚点定位不改变url同时平滑的滑动到锚点位置,不会生硬的直接到锚点位置
    简单的mock数据调试
    小程序textarea文本域字数控制---并显示已输入字数
  • 原文地址:https://www.cnblogs.com/fengkang1008/p/4739393.html
Copyright © 2020-2023  润新知