Linux sem参数配置,linux信号量的SEM_UNDO参数

本文详细介绍了SystemV信号量在进程间同步中的作用,特别是semop函数的P和V操作。通过示例程序展示了SEM_UNDO标志如何确保在进程异常结束时自动释放信号量,防止其他进程永久阻塞。实验结果显示,使用SEM_UNDO后,即使子进程被杀死,父进程仍能正常执行,保证了系统的正确运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号量是不同进程间或一个给定进程内部不同线程间同步的机制。System V信号量是一个或多个信号量的集合,其中的每一个都是氮素的计数信号量。System V信号量由内核维护,主要函数有:semget,semop,semctl。

我们重点来讨论semop函数,该函数的主要功能是对信号进行PV操作。

P操作负责把当前进程由运行状态转换为阻塞状态,知道另外一个进程唤醒它。操作为:申请一个空闲资源(把信号量减1),若成功,则退出,若失败,则该进程被阻塞。

V操作负责把被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息,操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒。

semop函数原型:

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

semop操作中:sembuf结构的sem_flg成员可以为0,IPC_NOWAIT,SEM_UNDO,为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。

下面我们用一段程序来看看SEM_UNDO的作用:

#include "comm.c"文件:

static int comm_creat_sem_set(int _sem_nums,int flag)

{

key_t _key=ftok(_PATH_,_PROJ_ID_);

if(_key<0)

{

perror("ftok");

return -1;

}

int sem_id=semget(_key,_sem_nums,flag);

if(sem_id<0)

{

return -1;

}

return sem_id;

}

int creat_sem_set(int _sem_nums)

{

umask(0);

int flag=IPC_CREAT|IPC_EXCL|0666;

return comm_creat_sem_set(_sem_nums,flag);

}

int get_sem_set(int _sem_nums)

{

int flag=IPC_CREAT;

return comm_creat_sem_set(_sem_nums,flag);

}

int init_sem_set(int _sem_id,int _sem_num,int _init_val)

{

union semun _un;

_un.val=_init_val;

if(semctl(_sem_id,_sem_num,SETVAL,_un)<0)

{

perror("semctl");

return -1;

}

return 0;

}

int p_sem(int _sem_id,int _seq_num)

{

struct sembuf _sem_buf[1];

_sem_buf[0].sem_num=_seq_num;

_sem_buf[0].sem_op=-1;

//  _sem_buf[0].sem_flg=SEM_UNDO;

_sem_buf[0].sem_flg=0;

if(semop(_sem_id,_sem_buf,1)<0)

{

perror("semop");

return -1;

}

return 0;

}

int v_sem(int _sem_id,int _seq_num)

{

struct sembuf _sem_buf[1];

_sem_buf[0].sem_num=_seq_num;

_sem_buf[0].sem_op=1;

_sem_buf[0].sem_flg=0;

//  _sem_buf[0].sem_flg=SEM_UNDO;

if(semop(_sem_id,_sem_buf,1)<0)

{

perror("semop");

return -1;

}

return 0;

}

comm.h文件:

#pragma once

#include

#include

#include

#include

#include

#include

#define _PATH_ "."

#define _PROJ_ID_ 0x6666

union semun

{

int val;    /* Value for SETVAL */

struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */

unsigned short  *array;  /* Array for GETALL, SETALL */

struct seminfo  *__buf;  /* Buffer for IPC_INFO*/

};

int creat_sem_set(int _sem_nums);

int get_sem_set(int _sem_nums);

int init_sem_set(int _sem_id,int _sem_nums,int _init_val);

int p_sem(int _sem_id,int _seq_num);

int v_sem(int _sem_id,int _seq_num);

int destroy_sem_set(int _sem_id);

sem.c文件:

int main()

{

int sem_id=creat_sem_set(1);

init_sem_set(sem_id,0,1);

pid_t id=fork();

if(id <0)

{

perror("fork");

exit(1);

}

else if(id==0)

{

int childid=getpid();

int fatherid=getppid();

printf("childid:%d,fatherid:%d\n",childid,fatherid);

int sem_id=get_sem_set(0);

while(1)

{

p_sem(sem_id,0);

printf("child wrinting\n");

sleep(1);

fflush(stdout);

printf("child finish post\n");

sleep(10);

fflush(stdout);

v_sem(sem_id,0);

}

}

else

{

while(1)

{

p_sem(sem_id,0);

printf("father wrinting\n");

sleep(1);

fflush(stdout);

printf("father finish post\n");

sleep(1);

fflush(stdout);

v_sem(sem_id,0);

}

}

destroy_sem_set(sem_id);

return 0;

}

在没用SEM_UNDO参数前,运行结果:

499238aef21131127d2380b4d90d2058.png

从代码中我们看到,在子进程P操作之后,我们让子进程sleep了10秒,在这之前我们把子进程傻掉了,也就是子进程没有进行V操作,所以没有释放占用的信号量,我们会看到,把子进程杀掉之后,父进程阻塞了。

而我们用了SEM_UNDO这个参数后,运行结果如下:

c83528160e8aeefba5ff854a3559594c.png

我们可以看到,在把子进程kill后,父进程照样可以运行,因为使用了SEM_UNDO后,操作系统自动释放该进程持有的信号量,从而可以使得另一个进程可以继续工作。否则,另外一个进程将永远阻塞。

Linux 中,可以使用 `semctl` 系统调用中的 `GETVAL` 命令来获取指定信号量的值。如果该值为0,则表示信号量已经被占用,没有可用的资源。 具体的代码实现可以参考如下示例: ```c #include <stdio.h> #include <stdlib.h> #include <sys/sem.h> int main() { int semid; struct sembuf sb; // 创建信号量semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } // 初始化信号量 if (semctl(semid, 0, SETVAL, 1) == -1) { perror("semctl"); exit(EXIT_FAILURE); } // 获取信号量的值 printf("Sem value: %d\n", semctl(semid, 0, GETVAL)); // 尝试获取信号量 sb.sem_num = 0; sb.sem_op = -1; // P 操作 sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop"); exit(EXIT_FAILURE); } printf("Semaphore acquired\n"); // 释放信号量 sb.sem_op = 1; // V 操作 if (semop(semid, &sb, 1) == -1) { perror("semop"); exit(EXIT_FAILURE); } printf("Semaphore released\n"); // 删除信号量集 if (semctl(semid, 0, IPC_RMID, 0) == -1) { perror("semctl"); exit(EXIT_FAILURE); } return 0; } ``` 在这个示例中,我们首先使用 `semget` 系统调用创建了一个信号量集,并使用 `semctl` 系统调用对该信号量进行了初始化。然后使用 `semctl` 系统调用获取了该信号量的值,并输出到控制台。 接下来,我们使用 `semop` 系统调用进行了一次 P 操作,即尝试获取该信号量。如果该信号量的值为0,则该操作将会被阻塞,直到信号量的值变为非0。在本例中,由于我们已经将信号量初始化为1,因此该 P 操作可以顺利完成。 最后,我们使用 `semop` 系统调用进行了一次 V 操作,即释放该信号量。然后使用 `semctl` 系统调用删除了该信号量集。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值