• 11.2 进程的同步和互斥


      顺序程序与并发程序的特征:

    顺序进程特征:

      顺序性

      封闭性(运行环境的封闭性)

      确定性

      可再现性

    并发程序特征:

      共享性

      并发性

      随机性

    进程互斥:

      由于各个进程要求共享资源,而且有些资源需要互斥使用,因此,各进程间竞争使用这些资源,进程的这种关系为进程的互斥。

      系统中的某些资源一次只允许一个进程使用,成这样的资源为临界资源或者互斥资源。

      在进程中涉及到互斥资源的程序段叫临界区。

    进程同步:

      进程同步指的是多个进程需要相互配合共同完成一项任务。

    进程间同步示例:

    司机启动车辆前,要先看看门是否关好,关好了才能启动车辆。而售票员在开门前要先看看车是否停好了,停好了的话才能开门。这些动作之间有一定的顺序,这就是同步。

     进程间通信的目的:

      数据传输:一个进程需要将它的数据发送给另一个进程

      资源共享:多个进程之间共享同样的资源

      通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)

      进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

    死锁:

    信号量:

    P、V操作的伪代码如下:

    信号量的数据结构如下:

    信号量相关的函数如下:

    semget函数:

      原型:int semget(key_t key,  int  nsems,  int  semflg)

      功能:用来访问和创建一个信号量集

      参数:

        key:信号集的名字

        nsems:信号集中信号量的个数

        semflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

        返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1

     semctl函数:

      原型:int  semctl(int semid,  int  semnum,  int  cmd, ...)

      功能:用于控制信号量集

      参数:

        semid:由semget返回的信号量集标识码

        semnum:信号集中信号量的序号

        cmd:将要采取的动作(有5个可取值)

        最后一个参数根据命令不同而不同

      返回值:成功返回0,失败返回-1

    可采取的动作如下:

     semop函数:

       原型:int  semop(int  semid,  struct  sembuf  *sops,  unsigned  nsops)

      功能:用来创建和访问一个信号量集

      参数:

        semid:信号量的标识码,就是semget函数的返回值

        sops:是一个指向结构体的指针

        nsops:信号量的个数

      返回值:成功返回0,失败返回-1

    sembuf结构如下:

    struct  sembuf

    {

      short  sem_num;

      short  sem_op;

      short  sem_flg;

    };

    sem_num:信号量的编号

    sem_op:信号量一次PV操作时加减的数值,一般只会用到两个值,P操作一般取-1, V操作取1

    sem_flag:有两个取值,IPC_NOWAIT和SEM_UNDO,当设置为SEM_UNDO时,若进程挂掉,则内核帮我们释放信号量,相当于进程挂掉时内核帮我们执行V操作

    semget示例程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <stdlib.h>
     6 #include <errno.h>
     7 #include <sys/msg.h>
     8 #include <sys/ipc.h>
     9 #include <sys/shm.h>
    10 #include <sys/types.h>
    11 #include <sys/ipc.h>
    12 #include <sys/sem.h>
    13 
    14 int main()
    15 {
    16     int semid = 0;
    17     semid = semget(0x1111, 1, 0666 | IPC_CREAT | IPC_EXCL);
    18     
    19     if(semid == -1)
    20     {
    21         if(errno == EEXIST)
    22         {
    23             printf("sem existed
    ");
    24         }
    25         
    26         perror("semget error");
    27         exit(0);
    28     }
    29     
    30     printf("semget success
    ");
    31     
    32     return 0;
    33 }

    执行结果如下:

    执行ipcs,结果如下:

    semctl示例程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <stdlib.h>
     6 #include <errno.h>
     7 #include <sys/msg.h>
     8 #include <sys/ipc.h>
     9 #include <sys/shm.h>
    10 #include <sys/types.h>
    11 #include <sys/ipc.h>
    12 #include <sys/sem.h>
    13 
    14 union semun {
    15    int              val;    /* Value for SETVAL */
    16    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    17    unsigned short  *array;  /* Array for GETALL, SETALL */
    18    struct seminfo  *__buf;  /* Buffer for IPC_INFO
    19                                (Linux-specific) */
    20 };
    21 
    22 
    23 int sem_creat(key_t key)
    24 {
    25     int semid = 0;
    26     semid = semget(key, 1, 0666 | IPC_CREAT | IPC_EXCL);
    27     
    28     if(semid == -1)
    29     {
    30         if(errno == EEXIST)
    31         {
    32             printf("sem existed
    ");
    33         }
    34         
    35         perror("semget error");
    36         return 0;
    37     }
    38     
    39     return semid;
    40 }
    41 
    42 
    43 int sem_open(key_t key)
    44 {
    45     int semid = 0;
    46     semid = semget(key, 1, 0666);
    47     
    48     if(semid == -1)
    49     {
    50         if(errno == EEXIST)
    51         {
    52             perror("open sem error");
    53             return -1;
    54         }
    55     }
    56     
    57     return semid;
    58 }
    59 
    60 int sem_setval(int semid, int val)
    61 {
    62     int ret = 0;
    63     union semun su;
    64     su.val = val;
    65     
    66     ret = semctl(semid, 0, SETVAL, su);
    67     
    68     return ret;
    69 }
    70 
    71 int sem_getval(int semid, int *val)
    72 {
    73     int ret = 0;
    74     union semun su;
    75     
    76     ret = semctl(semid, 0, GETVAL, su);
    77     
    78     if(ret != -1)
    79     {
    80         *val = su.val;
    81     }
    82     
    83     return ret;
    84 }
    85 
    86 int main()
    87 {
    88     //sem_creat(0x1111);
    89     int semid;
    90     semid = sem_open(0x1111);
    91     sem_setval(semid, 1);
    92     
    93     int val = 0;
    94     
    95     sem_getval(semid, &val);
    96     printf("val = %d
    ", val);
    97     
    98     return 0;
    99 }

    因为系统中已经有了key值为0x1111的信号量,所以我们直接打开这个信号量,执行结果如下:

     下一步我们封装P操作和V操作,程序如下:

      1 #include <sys/types.h>
      2 #include <unistd.h>
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include <errno.h>
      7 #include <sys/msg.h>
      8 #include <sys/ipc.h>
      9 #include <sys/shm.h>
     10 #include <sys/types.h>
     11 #include <sys/ipc.h>
     12 #include <sys/sem.h>
     13 
     14 union semun {
     15    int              val;    /* Value for SETVAL */
     16    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
     17    unsigned short  *array;  /* Array for GETALL, SETALL */
     18    struct seminfo  *__buf;  /* Buffer for IPC_INFO
     19                                (Linux-specific) */
     20 };
     21 
     22 
     23 int sem_creat(key_t key)
     24 {
     25     int semid = 0;
     26     semid = semget(key, 1, 0666 | IPC_CREAT | IPC_EXCL);
     27     
     28     if(semid == -1)
     29     {
     30         if(errno == EEXIST)
     31         {
     32             printf("sem existed
    ");
     33         }
     34         
     35         perror("semget error");
     36         return 0;
     37     }
     38     
     39     return semid;
     40 }
     41 
     42 
     43 int sem_open(key_t key)
     44 {
     45     int semid = 0;
     46     semid = semget(key, 1, 0666);
     47     
     48     if(semid == -1)
     49     {
     50         if(errno == EEXIST)
     51         {
     52             perror("open sem error");
     53             return -1;
     54         }
     55     }
     56     
     57     return semid;
     58 }
     59 
     60 int sem_setval(int semid, int val)
     61 {
     62     int ret = 0;
     63     union semun su;
     64     su.val = val;
     65     
     66     ret = semctl(semid, 0, SETVAL, su);
     67     
     68     return ret;
     69 }
     70 
     71 int sem_getval(int semid, int *val)
     72 {
     73     int ret = 0;
     74     union semun su;
     75     
     76     ret = semctl(semid, 0, GETVAL, su);
     77     
     78     if(ret != -1)
     79     {
     80         *val = su.val;
     81     }
     82     
     83     return ret;
     84 }
     85 
     86 int sem_p(int semid)
     87 {
     88     struct sembuf buf = {0, -1, 0};
     89     int ret = 0;
     90     
     91     ret = semop(semid, &buf, 1);
     92     return ret;
     93 }
     94 
     95 int sem_v(int semid)
     96 {
     97     struct sembuf    buf = {0, 1, 0};
     98     int ret = 0;
     99     
    100     ret = semop(semid, &buf, 1);
    101     return ret;
    102 }
    103 
    104 int main()
    105 {
    106     //sem_creat(0x1111);
    107     int semid;
    108     semid = sem_open(0x1111);
    109     sem_setval(semid, 1);
    110     
    111     int val = 0;
    112     
    113     sem_getval(semid, &val);
    114     sem_p(semid);
    115     printf("val = %d
    ", val);
    116     sem_v(semid);
    117     
    118     return 0;
    119 }

    我们封装了PV操作,109行将信号量的值设置为1,可以允许一个进程进入,执行结果如下:

    可以看到进程成功打印出了val的值,也就是成功进入了临界区。

    现在我们更改第109行,将信号量的初始值设置为0,再次执行程序,结果如下:

    由于运行完P操作之后进程进入睡眠状态,所以程序永远的停在了这里。

    semtool小工具:

    getopt函数提取命令行中的参数,存到opt中,第三个参数中的冒号(:)是一个占位符,后面的gfm用于提取第三个参数,然后放到optarg全局变量中。如果在命令行中找不到第三个参数中的值,会返回一个问号(?)。可以使用man 3 getopt查看具体用法。

  • 相关阅读:
    josn类库引用
    WPF圆角按钮
    C#实现某一属性值变化时触发事件 Form1_changeEvent是对应的事件
    C#winform生成安装包
    特性
    反射可以动态调用对象(一般是类)的名称,属性,方法等。具体见下。重要
    原子操作 和Inerlocked 常用于多线程同步
    spingboot 配置多个数据源报错
    Address already in use: JVM_Bind 端口被占用的几个解决办法
    数据库问题(一)
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9439608.html
Copyright © 2020-2023  润新知