Linux信号量函数

网友投稿 873 2022-05-29

1 信号量的概念

信号量,或称信号灯,其原理是一种数据操作锁的概念,本身不具备数据交换的功能,它负责协调各个进程,保证保证两个或多个关键代码段不被并发调用,确保公共资源的合理使用。信号量分为单值和多值两种。

在信号的实际应用中,是不能只定义一个信号量的,而只能定义一个信号量集,其中包含一组信号量,同一信号量集中的信号量使用同一个引用ID,这样的设置是为了多个资源或同步操作的需要。每个信号量集都有一个与之对应的结构,其中记录了信号量集的各种信息,该结构定义为:

struct semid_ds { struct ipc_perm sem_perm; // operation permission struct struct sem *sem_base; // ptr to first semaphore in set unsigned short sem_nsems; // numbers of semaphore in set time_t sem_otime; // last semop time time_t sem_ctime; // last change time };

ipc_perm结构体包含了信号量集的键值、所有者信息及操作权限等,定义如下:

struct ipc_perm { __key_t __key; // key value __uid_t uid; // owner uid __gid_t gid; // owner gid __uid_t cuid; // creater uid __gid_t cgid; // creater gid unsigned short int mode; // read/write permission unsigned short int __seq; // seq number ...... };

sem结构体记录了一个信号量的信息,定义如下:

struct sem { unsigned short semval; // semaphore value, always >= 0 pid_t sempid; // pid for last operation unsigned short semncent; // numbers of processes awaiting semval > currval unsigned short semzcnt; // numbers of processes awaiting semval = 0 };

2 信号量集的相关操作

2.1 创建/打开信号量集

使用semget()函数创建或打开一个信号量集,其函数原型为:

#include #include #include int semget(key_t key, int nsems, int flag);

运行成功返回信号量集的ID号,失败返回-1。

参数key为一个键值,可通过ftok()函数生成,参数nsems为创建的信号量集中包含的信号量个数,参数flag为操作参数,具体使用方法与创建共享内存的shmget()使用类似。

2.2 对信号量集的操作

使用semop()函数操作一个信号量集,其函数原型为:

#include #include #include int semop(int semid, struct sembuf semoparray[], size_t nops);

运行成功返回0,失败返回-1。

参数semid为semget()函数返回的信号量集ID号,参数semoparray是一个struct sembuf结构类型的数组,参数nops为前一数组参数的元素个数,sembuf的定义如下:

struct sembuf { unsigned short sem_num; short sem_op; short sem_flg; }

其中sem_num是信号集中的某一资源(即指定操作的信号量),其取值为0到资源总数(ipc_perm.sem_nsems)之间的整数;sem_op为一个整数,指明要执行的操作;sem_flg说明函数semop()的行为。

sem_op > 0:表示进程对资源使用完毕,释放相应的资源数,并将sem_op的值加到信号量的值上。

sem_op = 0:进程阻塞直到信号量的相应值为0,当信号量已经为0,函数立即返;信号量值不为0,则依据sem_flg的IPC_NOWAIT位决定函数动作:

sem_flg指定IPC_NOWAIT,则semop()函数出错返回EAGAIN;

sem_flg没有指定IPC_NOWAIT,则将信号量的semncnt的值减1,然后进程挂起直到下述情况发生:

信号量为0,则将信号量的semncnt的值加1,函数semop()成功返回;

信号量被删除,函数semop()出错返回EIDRM;

进程捕捉到信号,并从信号处理函数返回,信号量的semncnt的值减1,函数semop()出错返回EINTR。

sem_op < 0:请求sem_op的绝对值的资源,若相应的资源数可以满足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回;若资源数不足,这个操作与sem_flg有关:

sem_flg指定IPC_NOWAIT,则semop()函数出错返回EAGAIN;

sem_flg没有指定IPC_NOWAIT,则将信号量的semncnt的值加1,然后进程挂起直到下述情况发生:

资源数满足请求,信号值减去sem_op的绝对值,成功返回;

信号量被删除,函数semop()出错返回EIDRM;

进程捕捉到信号,并从信号处理函数返回,信号量的semncnt的值减1,函数semop()出错返回EINTR。

2.3 信号量集的控制

与共享内存的控制相似,信号量集也有自己的控制函数semctl(),函数原型如下:

#include #include #include int semctl(int semid, int semnum, int cmd, union semun arg);

Linux信号量及函数

运行成功返回大于等于0的值,失败返回-1,并设置错误变量errno。

参数semid为semget()函数返回的信号量集ID号,参数semnum指定信号量集中的某一信号量,类似于下标索引,参数cmd定义函数的操作,具体含义与后面的参数arg有关,arg是一个结构体,定义如下:

union semun { int val; struct semid_ds *buf; unsigned short array; struct seminfo *__buf; };

cmd的取值含义如下:

3 编程示例

示例1:信号量创建(内含ftok生成key原理)

创建信号量示例,sem_create.c:

#include #include #include #include #include #include #include #define PATHNAME "/tmp" int main(int argc, char *argv[]) { key_t keyid; int sigid; int nsems, semflg; nsems = 1; semflg = IPC_CREAT|0666; // below 4 lines code not necessary, just show the PATHNANE -> dev & inode, // then combine with proj_id to calculate the keyid (ftok work principle) struct stat stat_info; stat(PATHNAME, &stat_info); printf("dev:%lx\n", stat_info.st_dev); printf("inode:%lx\n", stat_info.st_ino); // generate key if((keyid = ftok(PATHNAME, 1)) == -1) { perror("ftok() failed\n"); exit(1); } else printf("key is %x\n", keyid); // create a semaphore if((sigid = semget(keyid, nsems, semflg)) == -1) { perror("semget() failed\n"); exit(1); } printf("sem create ok!\n"); return 0; }

运行结果:

$ ./sem_create dev:804 inode:280001 key is 1040001 sem create ok!

这里说明一下ftok()函数生成key的原理,ftok()定义如下:

key_t ftok(const char *pathname, int proj_id);

首先根据一个任意存在的pathname路径提取其所属文件系统的特有信息,包括设备号(stat.st_dev)和inode号(stat.st_ino),其获取方法参见上述程序中的sata结构,然后再结合ftok()函数的proj_id参数,按照如下规则计算key:

key1 = stat.st_ino & 0xffff; // 保留低16位 key2 = stat.st_dev & 0xff; // 保留低8位 key2 << = 16; // 左移16位 key3 = proj_id & 0xff; // 保留低8位 key3 << = 24; // 左移24位 key = key1|key2|key3; // 三者进行或运算

本例中,pathname=’/tmp‘,ftok()函数提取的设备号为0x804,inode号为0x280001,proj_id为0x01,根据以上运算过程,key1=0x01,key2=0x40000,key3=0x1000000,三者求或得到key=0x1040001,与程序输出的结果一致。

示例2:服务器-客户端模式

服务器端,信号量的创建与初始化,监视信号量资源使用情况,sem_server.c:

#include #include #include #include #include #include #define PATHNAME "/tmp" #define RESOURCE 3 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; int main(int argc, char *argvp[]) { key_t keyid; int sigid; int nsems, semflg; union semun semopt; struct sembuf sbuf = {0, 0, SEM_UNDO}; nsems = 1; semflg = IPC_CREAT|0666; // generate key if((keyid = ftok(PATHNAME, 3)) == -1) { perror("ftok() failed\n"); exit(1); } else printf("key is %x\n", keyid); // create a semaphore if((sigid = semget(keyid, nsems, semflg)) == -1) { perror("semget() failed\n"); exit(1); } else printf("create semaphore ok\n"); // initial the semaphore semopt.val = RESOURCE; if(semctl(sigid, 0, SETVAL, semopt) == -1) { perror("semctl() failed\n"); exit(1); } else printf("initial the semphore ok\n"); // monitor the semphore usage status while(1) { if(semop(sigid, &sbuf, 1) == 0)// use up!!! printf("resources have been exhausted\n"); sleep(3); } return 0; }

客户端程序,使用按键p和v模拟P操作和V操作,sem_client.c:

#include #include #include #include #include #include #define PATHNAME "/tmp" #define RESOURCE 3 int main(int argc, char *argvp[]) { key_t keyid; int sigid; int nsems, semflg; char opt; struct sembuf pbuf = {0, -1, IPC_NOWAIT}; // P operate struct sembuf vbuf = {0, 1, IPC_NOWAIT}; // V operate nsems = 1; semflg = IPC_CREAT|0666; // generate key if((keyid = ftok(PATHNAME, 3)) == -1) { perror("ftok() failed\n"); exit(1); } else printf("key is %x\n", keyid); // get the semaphore if((sigid = semget(keyid, nsems, semflg)) == -1) { perror("semget() failed\n"); exit(1); } else printf("get the semaphore ok\n"); // use p/v to simulate the PV operation while(1) { opt = getchar(); switch(opt) { case 'p':// -- if(semop(sigid, &pbuf, 1) == -1) printf("-->resources have been exhausted\n");// exhausted else printf("-->Total unused resources:%d\n", semctl(sigid, 0, GETVAL, 0)); break; case 'v':// ++ if(semop(sigid, &vbuf, 1) == -1) printf("-->V opreation failed\n"); else if(semctl(sigid, 0, GETVAL, 0) > RESOURCE) { printf("-->resources to achieve maximum\n");// achieve maximum semop(sigid, &pbuf, 1); } else printf("-->Total unused resources:%d\n", semctl(sigid, 0, GETVAL, 0)); break; case 's':// show printf("-->Total unused resources:%d\n", semctl(sigid, 0, GETVAL, 0)); break; case 'q':// quit exit(0); } sleep(1); } return 0; }

编译上面两个程序并执行,先运行服务器端程序:

$ ./sem_server key is 3040001 create semaphore ok initial the semphore ok

可以看到信号量创建并初始化成功,进入监视状态,在另一个shell中运行客户端程序:

$ ./sem_client key is 3040001 get the semaphore ok p -->Total unused resources:2 p -->Total unused resources:1 p -->Total unused resources:0 v -->Total unused resources:1 s -->Total unused resources:1 q

运行后,使用键盘的p和v键入shell,继续观察服务器端的结果,当客户端多次使用p键使资源耗尽后,服务器端会每隔3秒进行相应的输出提示报警,客户端使用v键增加资源,服务器端停止报警。

$ ./sem_server key is 3040001 create semaphore ok initial the semphore ok resources have been exhausted resources have been exhausted resources have been exhausted

最后可以使用Ctrl+c终止服务器端程序。

示例3

获取各种信号量的信息,利用信号量实现共享资源的申请和释放,sem_app.c:

#include #include #include #include #include #include #include #include #define SEM_PATH "." #define max_tries 3 int semid; union semun { int val; struct semid_ds *buf; unsigned short array; struct seminfo *__buf; }; int main(void) { int flag1, flag2, key, i, init_ok, tmperrno; struct semid_ds sem_info; struct seminfo sem_info2; union semun arg; struct sembuf askfor_res, free_res; flag1 = IPC_CREAT|IPC_EXCL|00666; flag2 = IPC_CREAT|00666; // generate key key = ftok(SEM_PATH, 'a'); if(key == -1) { perror("ftok error"); exit(1); } // create a semaphore init_ok = 0; semid = semget(key, 1, flag1); if(semid < 0) { tmperrno = errno; perror("semget"); if(tmperrno == EEXIST) { // semid = semget(key, 1, flag2); arg.buf = &sem_info; for(i=0; isem_otime!=0) { i = max_tries; init_ok = 1; } else sleep(1); } } if(!init_ok) { arg.val = 1; if(semctl(semid, 0, SETVAL, arg) == -1) perror("semctl setval error"); } } else { perror("semget error, process exit"); exit(1); } } else // semid >= 0, do some initializing { arg.val = 1; if(semctl(semid, 0, SETVAL, arg) == -1) perror("semctl setval error"); } // get some information about the semaphore and the limit of semaphore in Linux arg.buf = &sem_info; if(semctl(semid, 0, IPC_STAT, arg) == -1) perror("semctl IPC_STAT"); printf("owner's uid is %d\n", arg.buf->sem_perm.uid); printf("owner's gid is %d\n", arg.buf->sem_perm.gid); printf("creater's uid is %d\n", arg.buf->sem_perm.cuid); printf("creater's gid is %d\n", arg.buf->sem_perm.cgid); arg.__buf = &sem_info2; if(semctl(semid, 0, SEM_INFO, arg) == -1) perror("semctl IPC_INFO"); printf("the number of entries in semaphore map is %d\n", arg.__buf->semmap); printf("max number of semaphore identifiers is %d\n", arg.__buf->semmni);//o); printf("mas number of semaphore in system is %d\n", arg.__buf->semmns); printf("the number of undo structures system wide is %d\n", arg.__buf->semmnu); printf("max number of semaphore per semid is %d\n", arg.__buf->semmsl); printf("max number of ops per semop call is %d\n", arg.__buf->semopm); printf("max number of undo entries per process is %d\n", arg.__buf->semume); printf("the size of struct sem_undo is %d\n", arg.__buf->semusz); printf("the maximum semaphore value is %d\n", arg.__buf->semvmx); // now ask for available resource askfor_res.sem_num = 0; askfor_res.sem_op = -1; askfor_res.sem_flg = SEM_UNDO; if(semop(semid, &askfor_res, 1) == -1) perror("semop error"); sleep(3); printf("now free the resource\n"); // now free resource free_res.sem_num = 0; // resources num free_res.sem_op = 1; free_res.sem_flg = SEM_UNDO; if(semop(semid, &free_res, 1) == -1) if(errno == EIDRM) printf("the semaphore set was removed\n"); if(semctl(semid, 0, IPC_RMID) == -1) perror("semctl IPC_RMID"); else printf("remove sem ok\n"); return 0; }

编译运行:

$ ./sem_app owner's uid is 1000 owner's gid is 1000 creater's uid is 1000 creater's gid is 1000 the number of entries in semaphore map is 1024000000 max number of semaphore identifiers is 32000 mas number of semaphore in system is 1024000000 the number of undo structures system wide is 1024000000 max number of semaphore per semid is 32000 max number of ops per semop call is 500 max number of undo entries per process is 500 the size of struct sem_undo is 1 the maximum semaphore value is 32767 now free the resource remove sem okt

linux 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:C语言函数章节实战总结: 重写部分字符串库函数
下一篇:C++从入门到精通(第一篇) :C++的入门(基础语法的整理)
相关文章