【Linux】进程间通信 -- 信号量

  • 概念引入
  • 为什么要信号量?
  • 信号量操作接口
    • 1.申请信号量semget
    • 2.控制信号量semctl
    • 3.处理信号量semop
  • IPC资源的组织方式

概念引入

信号量是什么?
本质是一个计数器,通常用来表示公共资源中,资源数量多少的问题

公共资源:能被多个进程同时可以访问的资源

访问没有保护的公共资源:数据不一致问题(比如我想写abc123,但是我123还没有写入,就读取了abc,可能数据分开会导致数据无意义)

为什么要让不同的进程看到同一份资源呢?

因为我想通信,进程间实现协同。但是进程具有独立性,没有办法让两个进程直接通信,为了解决这种问题,解决办法就是让进程看到同一份资源,但是因为提出了这个方法,同时也引入了新的问题:数据不一致

我们将保护起来的公共资源称为:临界资源(大部分资源是独立的)

资源包括内存、文件、网络等,资源存在的意义就是要被使用,如何被进程使用呢?
一定是该进程有对应的代码来访问这部分临界资源(有临界区就有非临界区)

多进程在进行进程间通信时本质是要看到一份公共的资源,这部分公共资源在未来如果被保护起来,那么这部分公共资源我们就称其为临界资源,而访问该临界资源中我们自己的那部分代码我们叫做临界区,不访问临界资源那部分代码的我们叫做非临界区

如何保护资源:互斥与同步
原子性:要么不做,要做就做完,只有两种状态(不会被线程调度机制打断的操作)

为什么要信号量?

举例认识:比如我们看电影,我们不是只要坐在一个座位上,那么这个座位就属于你了,而是你要去买票,然后给你分配一个座位,这个座位才属于你,不管你去不去,你只要买票了,那么这个座位始终都会留给你,这种电影买票的本质:对放映厅中的座位进行预定机制。我们想要某种资源的时候也可以进行预定。

共享资源的使用:作为一个整体使用、划分为一个一个的子资源使用
我们想让不同的进程同时访问一份资源的不同区域,这样我们某种程度上能实现并发

假如我们将一份共享资源(电影院)分成几份,进程(人),访问共享资源的子部分(电影的座位),我们不能直接去访问这些资源(不能不买票随便坐),信号量(电影票),每个进程先申请信号量,如果申请成功了,就相当于预定这部分资源(买到票有座位),申请失败就不允许访问这部分资源(没买票到不允许看电影)。这种申请信号量方式以达到保护共享资源,约束其他进程的目的。信号量就是计数器(电影票是有限的),申请一个就少一个。

sem = 20;//定义信号量总数

sem--;  //预定资源

//访问公共资源

sem++;  //释放资源

PV操作:P → 预定资源 V → 释放资源

所有的进程在访问公共资源之前,都必须先申请sem信号量→必须先申请sem信号量的前提,是所有进程必须先得看到同一个信号量→信号量本身就是公共资源→信号量是不是也要保证自己的安全呢?- -,++→信号量必须保证自身操作的安全性,- -,++操作是原子! !

如果一个信号量初始值为1:二元信号量–互斥功能

思考:有没有可能两个进程都申请信号量成功了却访问同一个资源?
有的,申请信号量成功只能表明这个资源一定有部分是留给你访问的,但具体是哪一部分还需要确认。这部分的工作一般是由程序员来指定的


信号量操作接口

1.申请信号量semget

【Linux】进程间通信 -- 信号量_第1张图片
函数原型:

int semget(key_t key, int nsems, int semflg);

函数参数:

  1. key:一个用于标识信号量集的键值,它必须是一个非零的整数。通常使用 ftok 函数来生成键值。
  2. nsems:信号量集中包含的信号量的数量。这个参数必须是一个大于零的整数。
  3. semflg:一个标志参数,用于指定信号量集的创建和访问权限。常见的标志包括:
    IPC_CREAT:如果指定的 key 值对应的信号量集不存在,则创建它。
    IPC_EXCL:如果同时指定了 IPC_CREAT 和 IPC_EXCL 标志,而对应的信号量集已经存在,则会出现错误。
    0666:指定新创建的信号量集的权限。这个参数使用八进制表示,表示该信号量集可以被所有用户读写。

函数返回值:
semget 函数的返回值是一个信号量集的标识符,也称为“信号量集描述符”。它是一个非负整数,可以用于后续的信号量操作函数中。

2.控制信号量semctl

【Linux】进程间通信 -- 信号量_第2张图片
函数原型:

int semctl(int semid, int semnum, int cmd, ...);

函数参数:

  1. semid:一个信号量集的标识符,它是由 semget 函数返回的。

  2. semnum:要操作的信号量在信号量集中的编号,从 0 开始计数。

  3. cmd:用于指定要执行的操作,常见的操作包括:
    GETVAL:获取指定信号量的当前值。
    SETVAL:设置指定信号量的值。
    IPC_RMID:删除指定的信号量集。
    IPC_STAT:获取指定信号量集的状态信息。
    IPC_SET:设置指定信号量集的状态信息。

  4. ... :可选参数,用于给 SETVAL 命令指定要设置的值。

函数返回值根据不同的命令而异:

  • 对于 GETVAL 命令,返回指定信号量的当前值。
  • 对于 SETVAL 命令,返回 0 表示操作成功,-1 表示操作失败。
  • 对于 IPC_RMID、IPC_STAT 和 IPC_SET 命令,返回 0 表示操作成功,-1 表示操作失败。

3.处理信号量semop

【Linux】进程间通信 -- 信号量_第3张图片

int semop(int semid, struct sembuf *sops, unsigned nsops);

函数参数:

  1. semid:一个信号量集的标识符,它是由 semget 函数返回的。
  2. sops:一个指向 sembuf 结构体数组的指针,每个结构体描述一个操作。sembuf 结构体定义如下:
struct sembuf {
    unsigned short sem_num;  // 操作的信号量在信号量集中的编号
    short sem_op;            // 操作的值
    short sem_flg;           // 操作的标志
};
- `sem_num`:要操作的信号量在信号量集中的编号,从 0 开始计数。

- `sem_op`:要执行的操作,可以是一个正数、负数或零。

  - 如果 `sem_op` 是一个正数,表示对应的信号量的值将增加 `sem_op`。

  - 如果 `sem_op` 是一个负数,表示对应的信号量的值将减少 `sem_op`。

  - 如果 `sem_op` 是零,表示对应的信号量的值不变。

- `sem_flg`:操作的标志,包括:

  - `SEM_UNDO`:表示在进程异常终止时,内核将撤销该进程所做的所有操作。

  - `IPC_NOWAIT`:表示如果无法立即执行相应的操作,则立即返回。
  1. nsops:sops 数组中元素的数量。

函数返回值:
表示是否操作成功,如果成功则返回 0,如果失败则返回 -1。

IPC资源的组织方式

共享内存、消息队列、信号量它们的接口相似度很高,比如获取与删除

且它们开始都是定义:

struct ipc_perm {
    key_t          __key;    /* Key supplied to shmget(2) */
    uid_t          uid;      /* Effective UID of owner */
    gid_t          gid;      /* Effective GID of owner */
    uid_t          cuid;     /* Effective UID of creator */
    gid_t          cgid;     /* Effective GID of creator */
    unsigned short mode;     /* Permissions + SHM_DEST and
                                SHM_LOCKED flags */
    unsigned short __seq;    /* Sequence number */
};

【Linux】进程间通信 -- 信号量_第4张图片
对其进行操作:

(struct shmid_ds*)perms[0]->其他的属性

struct shmid_di myshm;
perms[0] = &myshm.shm_perm;

struct msqid_ds mymsg;
perms[1] = &mymsg.msg_perm;

结构体的第一个成员的地址,在数字上和结构体对象本身的地址数字是相等的,我们就可以通过结构体的地址,强转struct shmid_ds*来使用其里面的其他属性,如上述伪代码。


如有错误或者不清楚的地方欢迎私信或者评论指出

你可能感兴趣的:(Linux基础,linux,服务器,运维,进程间通信,信号量)