linux 进程 信号量

1. 概述:
该demo主要实现linux下进程之间信号量的使用, 相关接口介绍可以参考<<UNIX环境高级编程>>
在这里插入图片描述

2. 测试:
在这里插入图片描述

/*
demo_process_IPC_semget.c
进程编程demo(信号量)

IPC相关的命令: ipcs ipcrm(释放IPC)
查看进程属性: ulimit -a

*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/prctl.h>

/*
头文件 sem.h 把 union semun 声明注释掉了
*/
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};

/*
二元信号量操作
*/
static int Semctl_Init(int semid);
static void Semctl_Deit(int semid);
static int Semop_Down(int semid);
static int Semop_Up(int semid);

#define SEM_ID                  1

int main(int argc, char **argv){

    int ret;
    pid_t pid;
    int semid = -1;
    key_t key_sem = SEM_ID;

    /*
    num_sems : 信号量集合数目, 通常它的值为1(二元信号量)
    sem_flags : IPC_CREAT | IPC_EXCL, 如果没有该信号量, 则创建, 并返回信号量ID
                若已有该信号量, 则返回-1
    */
    semid = semget(key_sem, 1, 600 | IPC_CREAT | IPC_EXCL);
    if(semid < 0){
        /*printf("semget:%s\n", strerror(errno));*/
        perror("semget:");
        return -1;
    }

    ret = Semctl_Init(semid);
    if(ret < 0){
        printf("Semctl_Init error\n");
        return -1;
    }

    pid = fork();
    if(pid < 0){
        perror("fork:");
        Semctl_Deit(semid);
        return -1;
    }else if(pid == 0){

        prctl(PR_SET_NAME, "child_process");

        printf("child process\n");    /*子进程*/

        ret = Semop_Down(semid);
        if(ret < 0){
            printf("Semop_Down error\n");
            return -1;
        }

        printf("child process\t Start\n");
        sleep(1);
        printf("child process\t Stop\n");

        ret = Semop_Up(semid);
        if(ret < 0){
            printf("Semop_Up error\n");
            return -1;
        }
    }else{
        /*
        进程重命名
        */
        prctl(PR_SET_NAME, "parant_process");

        printf("parant process\n");    /*父进程*/

        ret = Semop_Down(semid);
        if(ret < 0){
            printf("Semop_Down error\n");
            return -1;
        }

        printf("parant process\t Start\n");
        sleep(1);
        printf("parant process\t Stop\n");

        ret = Semop_Up(semid);
        if(ret < 0){
            printf("Semop_Up error\n");
            return -1;
        }

        wait(NULL);

        Semctl_Deit(semid);
    }

    return 0;
}

/*
初始化信号量
*/
static int Semctl_Init(int semid)
{
    union semun sem_union;

    /*
    semnum : 信号量集合的第semnum个信号量
    SETVAL : 初始化信号量, 通过union semun中的val成员设置
    第四个参数是可选的, 取决于第三个参数
    */
    sem_union.val = 1;
    if (semctl(semid, 0, SETVAL, sem_union) < 0){
        perror("semctl:");
        return -1;
    }
    return 0;
}

/*
删除信号量
*/
static void Semctl_Deit(int semid)
{
    /*
    semnum : 信号量集合的第semnum个信号量
    IPC_RMID : 删除信号量
    第四个参数是可选的, 取决于第三个参数
    */
    if (semctl(semid, 0, IPC_RMID) < 0){
        perror("semctl:");
    }
}

/*
获取信号量资源
*/
static int Semop_Down(int semid)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;            /* 除非使用一组信号量, 否则它为0 */
    sem_b.sem_op = -1;            /* 若sem_op为负, 表示要获取该信号量控制的资源数
                                    信号量值减去sem_op的绝对值 (P操作) */
    sem_b.sem_flg = SEM_UNDO;    /* 通常为SEM_UNDO, 使操作系统跟踪信号
                                    即程序结束时(不论正常或不正常), 保证信号值会被重设为semop()调用前的值
                                    这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁, 造成该资源永远锁定 */

    if (semop(semid, &sem_b, 1) < 0){
        perror("semop:");
        return -1;
    }

    return 0;
}

/*
释放信号量资源
*/
static int Semop_Up(int semid)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;             /* 若sem_op为正, 表示要释放信号量占用的资源数
                                    信号量值加上sem_op的值 (V操作) 
                                    附 : 若sem_op为0, 表示将信号量值置为0*/
    sem_b.sem_flg = SEM_UNDO;

    if (semop(semid, &sem_b, 1) < 0){
        perror("semop:");
        return -1;
    }

    return 0;
}
#Makefile
CC := gcc
INCLUDE = -I /home/demo/include/

all:
	$(CC) demo_process_IPC_semget.c $(INCLUDE) -o demo_process_IPC_semget -Wall -Werror

clean:
	rm demo_process_IPC_semget
在多线程或多进程并发编程领域,确保对共享资源的安全访问和协调不同执行单元的同步至关重要,而信号量作为经典的同步原语之一,在 Linux 系统中扮演着核心角色[^1]。 ### 信号量的概念 信号量是用于控制对共享资源访问的一种机制,根据信号量取值(代表可用资源的数目)的不同,信号量可分为二值信号量和计数信号量。二值信号量的值只有 0 和 1,类似于互斥量,若资源被锁住,信号量的值为 0,若资源可用,则信号量的值为 1;计数信号量的值在 0 到一个大于 1 的限制值(POSIX 指出系统的最大限制值至少要为 32767),该计数表示可用的资源的个数[^3]。 ### 信号量的操作 信号量有两个基本操作,即 P 操作(Wait 操作)和 V 操作(Signal 操作)。P 操作会尝试减少信号量的值,如果信号量的值大于 0,则将其减 1 并继续执行;如果信号量的值为 0,则进程会被阻塞,直到信号量的值大于 0。V 操作会增加信号量的值,如果有进程因等待该信号量而被阻塞,则唤醒其中一个进程[^2]。 ### POSIX 信号量 API 使用Linux 系统中,POSIX 信号量提供了一系列的 API 来进行信号量的操作,主要包括以下几个函数: 1. `sem_init()`:用于初始化一个未命名的信号量。 ```c #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); ``` - `sem`:指向要初始化的信号量的指针。 - `pshared`:表示信号量的共享方式,0 表示该信号量只能在当前进程的多个线程间共享,非 0 表示可以在不同进程间共享。 - `value`:信号量的初始值。 2. `sem_destroy()`:用于销毁一个未命名的信号量。 ```c #include <semaphore.h> int sem_destroy(sem_t *sem); ``` - `sem`:指向要销毁的信号量的指针。 3. `sem_wait()`:实现 P 操作。 ```c #include <semaphore.h> int sem_wait(sem_t *sem); ``` - `sem`:指向要操作的信号量的指针。 4. `sem_post()`:实现 V 操作。 ```c #include <semaphore.h> int sem_post(sem_t *sem); ``` - `sem`:指向要操作的信号量的指针。 ### 示例代码 以下是一个简单的基于信号量的生产者 - 消费者模型的示例代码: ```c #include <stdio.h> #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int in = 0; int out = 0; sem_t empty; sem_t full; pthread_mutex_t mutex; void *producer(void *arg) { int item; for (int i = 0; i < 10; i++) { item = i; sem_wait(&empty); pthread_mutex_lock(&mutex); buffer[in] = item; printf("Produced: %d at position %d\n", item, in); in = (in + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); sem_post(&full); } pthread_exit(NULL); } void *consumer(void *arg) { int item; for (int i = 0; i < 10; i++) { sem_wait(&full); pthread_mutex_lock(&mutex); item = buffer[out]; printf("Consumed: %d from position %d\n", item, out); out = (out + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); sem_post(&empty); } pthread_exit(NULL); } int main() { pthread_t producer_thread, consumer_thread; sem_init(&empty, 0, BUFFER_SIZE); sem_init(&full, 0, 0); pthread_mutex_init(&mutex, NULL); pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); sem_destroy(&empty); sem_destroy(&full); pthread_mutex_destroy(&mutex); return 0; } ``` ### 注意事项 - 在使用信号量时,要确保正确地初始化和销毁信号量,避免资源泄漏。 - 对于 P 操作和 V 操作的调用顺序要正确,否则可能会导致死锁等问题。 - 在多进程间共享信号量时,要注意信号量的共享方式的设置。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值