linux SysV IPC sem信号量实现
来源:互联网 发布:coc治疗药水数据 编辑:程序博客网 时间:2024/05/29 19:56
信号量值是一个计数器,用于控制多进程对共享资源的访问;进程获取共享资源需要以下步骤:
1.检查资源的信号量值
2.如果信号量值是正,进程可以访问资源;进程将信号量值减1,表示进程已经使用了一个资源
3.如果信号量值是0,进程被阻塞直到信号量值大于0;被阻塞进程被唤醒时重复执行步骤1
注:检查信号量值和减1必须为原子操作
SysV信号量有以下特征:
1.信号量是一个信号量值集合,在创建信号量时指定信号量值个数;由于存在多个信号量值,所以操作信号量时必须是原子操作;
2.SysV IPC在没有进程使用时依然存在,所以要考虑在进程退出时归还进程获得的信号量值;
3.当信号量值不满足要求时,必须阻塞进程;当信号量满足要求时,唤醒被阻塞的进程
I.数据结构
i.信号量(信号量值集合)
include/linux/sem.h 85 /* One semaphore structure for each semaphore in the system. */ 86 struct sem { 87 int semval; /* current value */ 88 int sempid; /* pid of last operation */ 89 }; 90 91 /* One sem_array data structure for each set of semaphores in the system. */ 92 struct sem_array { 93 struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */ 94 time_t sem_otime; /* last semop time */ 95 time_t sem_ctime; /* last change time */ 96 struct sem *sem_base; /* ptr to first semaphore in array */ 97 struct list_head sem_pending; /* pending operations to be processed */ 98 struct list_head list_id; /* undo requests on this array */ 99 unsigned long sem_nsems; /* no. of semaphores in array */100 };
ii.信号量阻塞进程队列
/* semop system calls takes an array of these. */struct sembuf { unsigned short sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */};102 /* One queue for each sleeping process in the system. */103 struct sem_queue {104 struct list_head list; /* queue of pending operations */105 struct task_struct *sleeper; /* this process */106 struct sem_undo *undo; /* undo structure */107 int pid; /* process id of requesting process */108 int status; /* completion status of operation */109 struct sembuf *sops; /* array of pending operations */110 int nsops; /* number of operations */111 int alter; /* does the operation alter the array? */112 };
iii.进程信号量UNDO队列(用于进程退出时归还获取的信号量值)
114 /* Each task has a list of undo requests. They are executed automatically115 * when the process exits.116 */117 struct sem_undo {118 struct list_head list_proc; /* per-process list: all undos from one process. */119 /* rcu protected */120 struct rcu_head rcu; /* rcu struct for sem_undo() */121 struct sem_undo_list *ulp; /* sem_undo_list for the process */122 struct list_head list_id; /* per semaphore array list: all undos for one array */123 int semid; /* semaphore set identifier */124 short * semadj; /* array of adjustments, one per semaphore */125 };126 127 /* sem_undo_list controls shared access to the list of sem_undo structures128 * that may be shared among all a CLONE_SYSVSEM task group.129 */130 struct sem_undo_list {131 atomic_t refcnt;132 spinlock_t lock;133 struct list_head list_proc;134 };135 136 struct sysv_sem {137 struct sem_undo_list *undo_list;138 };include/linux/sched.h1217 struct task_struct {1364 struct sysv_sem sysvsem;1543 };
iv.结构之间关系
上述结构之间关系如下图:
II.信号量创建
信号量由newary来创建:
227 /** 228 * newary - Create a new semaphore set 229 * @ns: namespace 230 * @params: ptr to the structure that contains key, semflg and nsems 231 * 232 * Called with sem_ids.rw_mutex held (as a writer) 233 */ 234 235 static int newary(struct ipc_namespace *ns, struct ipc_params *params) 236 { 237 int id; 238 int retval; 239 struct sem_array *sma; 240 int size; 241 key_t key = params->key; 242 int nsems = params->u.nsems; 243 int semflg = params->flg; 244 245 if (!nsems) 246 return -EINVAL; 247 if (ns->used_sems + nsems > ns->sc_semmns) 248 return -ENOSPC; 249 250 size = sizeof (*sma) + nsems * sizeof (struct sem); 251 sma = ipc_rcu_alloc(size); 252 if (!sma) { 253 return -ENOMEM; 254 } 255 memset (sma, 0, size); 256 257 sma->sem_perm.mode = (semflg & S_IRWXUGO); 258 sma->sem_perm.key = key; 259 260 sma->sem_perm.security = NULL; 261 retval = security_sem_alloc(sma); 262 if (retval) { 263 ipc_rcu_putref(sma); 264 return retval; 265 } 266 267 id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); 268 if (id < 0) { 269 security_sem_free(sma); 270 ipc_rcu_putref(sma); 271 return id; 272 } 273 ns->used_sems += nsems; 274 275 sma->sem_base = (struct sem *) &sma[1]; 276 INIT_LIST_HEAD(&sma->sem_pending); 277 INIT_LIST_HEAD(&sma->list_id); 278 sma->sem_nsems = nsems; 279 sma->sem_ctime = get_seconds(); 280 sem_unlock(sma); 281 282 return sma->sem_perm.id; 283 }
1.参数“信号量值个数”合法性校验,及系统允许信号量值个数检验
2.分配信号量结构(包括信号量值集合),并初始化(信号量等待队列、undo队列置空,信号量创建时间等)
3.将信号量添加到信号量基数树中,并返回基数树id
III.信号量操作
i.原子操作try_atomic_semop
334 /* 335 * Determine whether a sequence of semaphore operations would succeed 336 * all at once. Return 0 if yes, 1 if need to sleep, else return error code. 337 */ 338 339 static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops, 340 int nsops, struct sem_undo *un, int pid) 341 { 342 int result, sem_op; 343 struct sembuf *sop; 344 struct sem * curr; 345 346 for (sop = sops; sop < sops + nsops; sop++) { 347 curr = sma->sem_base + sop->sem_num; 348 sem_op = sop->sem_op; 349 result = curr->semval; 350 351 if (!sem_op && result) 352 goto would_block; 353 354 result += sem_op; 355 if (result < 0) 356 goto would_block; 357 if (result > SEMVMX) 358 goto out_of_range; 359 if (sop->sem_flg & SEM_UNDO) { 360 int undo = un->semadj[sop->sem_num] - sem_op; 361 /* 362 * Exceeding the undo range is an error. 363 */ 364 if (undo < (-SEMAEM - 1) || undo > SEMAEM) 365 goto out_of_range; 366 } 367 curr->semval = result; 368 } 369 370 sop--; 371 while (sop >= sops) { 372 sma->sem_base[sop->sem_num].sempid = pid; 373 if (sop->sem_flg & SEM_UNDO) 374 un->semadj[sop->sem_num] -= sop->sem_op; 375 sop--; 376 } 377 378 sma->sem_otime = get_seconds(); 379 return 0; 380 381 out_of_range: 382 result = -ERANGE; 383 goto undo; 384 385 would_block: 386 if (sop->sem_flg & IPC_NOWAIT) 387 result = -EAGAIN; 388 else 389 result = 1; 390 391 undo: 392 sop--; 393 while (sop >= sops) { 394 sma->sem_base[sop->sem_num].semval -= sop->sem_op; 395 sop--; 396 } 397 398 return result; 399 }
对于单个信号量值来说:
1.semop<0,且信号量值<-semop,则进程被阻塞,直到信号量值>=-semop;
2.semop=0,且信号量值!=0,则进程被阻塞,直到信号量值==0
3.semop>0,信号量值+semop不得超过信号值最大值SEMVMX(32767)
4.semop!=0,信号量值+=semop;并检查undo范围
原子操作如下:
1.所有信号量操作,如果有一个会被阻塞或超出范围,则会回退修改的信号量值;并通知调用者睡眠或错误
2.所有信号量均不会被阻塞或超限,则保持修改的信号量值不变,并将SEM_UNDO标志的操作记录到undo结构中,以便进程退出时归还信号量值
ii.唤醒信号量阻塞进程
每次信号量有修改时,均会检查被阻塞进程的信号量操作,如果信号量当前值满足要求,则唤醒被阻塞的进程。
由update_queue实现:
401 /* Go through the pending queue for the indicated semaphore 402 * looking for tasks that can be completed. 403 */ 404 static void update_queue (struct sem_array * sma) 405 { 406 int error; 407 struct sem_queue * q; 408 409 q = list_entry(sma->sem_pending.next, struct sem_queue, list); 410 while (&q->list != &sma->sem_pending) { 411 error = try_atomic_semop(sma, q->sops, q->nsops, 412 q->undo, q->pid); 413 414 /* Does q->sleeper still need to sleep? */ 415 if (error <= 0) { 416 struct sem_queue *n; 417 418 /* 419 * Continue scanning. The next operation 420 * that must be checked depends on the type of the 421 * completed operation: 422 * - if the operation modified the array, then 423 * restart from the head of the queue and 424 * check for threads that might be waiting 425 * for semaphore values to become 0. 426 * - if the operation didn't modify the array, 427 * then just continue. 428 * The order of list_del() and reading ->next 429 * is crucial: In the former case, the list_del() 430 * must be done first [because we might be the 431 * first entry in ->sem_pending], in the latter 432 * case the list_del() must be done last 433 * [because the list is invalid after the list_del()] 434 */ 435 if (q->alter) { 436 list_del(&q->list); 437 n = list_entry(sma->sem_pending.next, 438 struct sem_queue, list); 439 } else { 440 n = list_entry(q->list.next, struct sem_queue, 441 list); 442 list_del(&q->list); 443 } 444 445 /* wake up the waiting thread */ 446 q->status = IN_WAKEUP; 447 448 wake_up_process(q->sleeper); 449 /* hands-off: q will disappear immediately after 450 * writing q->status. 451 */ 452 smp_wmb(); 453 q->status = error; 454 q = n; 455 } else { 456 q = list_entry(q->list.next, struct sem_queue, list); 457 } 458 } 459 }
注;
1.如果被唤醒的进程会修改信号量,则重新扫描信号量阻塞进程,因为修改信号量可以使前面扫描未满足要求的操作满足要求;如果不会修改信号量,则继续扫描以后的阻塞进程
2.进程的唤醒采用无锁方式:
阻塞过程:
a.置状态为EINTR
唤醒过程:
a.被阻塞进程从信号量阻塞队列中删除
b.置状态为IN_WAKEUP
c.唤醒被阻塞进程
d.置状态为终态
被唤醒进程检查状态:
a.状态是IN_WAKEUP,等待状态改变
b.状态是非EINTR,则调用update_queue去尝试唤醒其它被阻塞进程
c.其它情况,获取spinlock,检查进程被唤醒原因(如超时、信号量被删除等)
iii.信号量操作
信号量操作是原子操作,如果信号量操作不用睡眠且未出错,则尝试唤醒被阻塞进程。
如果需要睡眠,将进程添加到信号量阻塞队列,调度其它进程使用CPU;进程被唤醒时,检查是被正常唤醒(IN_WAKEUP-唤醒还没有来得及修改状态为终态,!EINTR-唤醒已经修改成终态)、异常唤醒(等待超时)
信号量操作实现如下:
1061 SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,1062 unsigned, nsops, const struct timespec __user *, timeout)1063 {1064 int error = -EINVAL;1065 struct sem_array *sma;1066 struct sembuf fast_sops[SEMOPM_FAST];1067 struct sembuf* sops = fast_sops, *sop;1068 struct sem_undo *un;1069 int undos = 0, alter = 0, max;1070 struct sem_queue queue;1071 unsigned long jiffies_left = 0;1072 struct ipc_namespace *ns;1073 1074 ns = current->nsproxy->ipc_ns;1075 1076 if (nsops < 1 || semid < 0)1077 return -EINVAL;1078 if (nsops > ns->sc_semopm)1079 return -E2BIG;1080 if(nsops > SEMOPM_FAST) {1081 sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);1082 if(sops==NULL)1083 return -ENOMEM;1084 }1085 if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {1086 error=-EFAULT;1087 goto out_free;1088 }1089 if (timeout) {1090 struct timespec _timeout;1091 if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {1092 error = -EFAULT;1093 goto out_free;1094 }1095 if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||1096 _timeout.tv_nsec >= 1000000000L) {1097 error = -EINVAL;1098 goto out_free;1099 }1100 jiffies_left = timespec_to_jiffies(&_timeout);1101 }1102 max = 0;1103 for (sop = sops; sop < sops + nsops; sop++) {1104 if (sop->sem_num >= max)1105 max = sop->sem_num;1106 if (sop->sem_flg & SEM_UNDO)1107 undos = 1;1108 if (sop->sem_op != 0)1109 alter = 1;1110 }1111 1112 if (undos) {1113 un = find_alloc_undo(ns, semid);1114 if (IS_ERR(un)) {1115 error = PTR_ERR(un);1116 goto out_free;1117 }1118 } else1119 un = NULL;1120 1121 sma = sem_lock_check(ns, semid);1122 if (IS_ERR(sma)) {1123 if (un)1124 rcu_read_unlock();1125 error = PTR_ERR(sma);1126 goto out_free;1127 }1128 1129 /*1130 * semid identifiers are not unique - find_alloc_undo may have1131 * allocated an undo structure, it was invalidated by an RMID1132 * and now a new array with received the same id. Check and fail.1133 * This case can be detected checking un->semid. The existance of1134 * "un" itself is guaranteed by rcu.1135 */1136 error = -EIDRM;1137 if (un) {1138 if (un->semid == -1) {1139 rcu_read_unlock();1140 goto out_unlock_free;1141 } else {1142 /*1143 * rcu lock can be released, "un" cannot disappear:1144 * - sem_lock is acquired, thus IPC_RMID is1145 * impossible.1146 * - exit_sem is impossible, it always operates on1147 * current (or a dead task).1148 */1149 1150 rcu_read_unlock();1151 }1152 }1153 1154 error = -EFBIG;1155 if (max >= sma->sem_nsems)1156 goto out_unlock_free;1157 1158 error = -EACCES;1159 if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))1160 goto out_unlock_free;1161 1162 error = security_sem_semop(sma, sops, nsops, alter);1163 if (error)1164 goto out_unlock_free;1165 1166 error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));1167 if (error <= 0) {1168 if (alter && error == 0)1169 update_queue (sma);1170 goto out_unlock_free;1171 }1172 1173 /* We need to sleep on this operation, so we put the current1174 * task into the pending queue and go to sleep.1175 */1176 1177 queue.sops = sops;1178 queue.nsops = nsops;1179 queue.undo = un;1180 queue.pid = task_tgid_vnr(current);1181 queue.alter = alter;1182 if (alter)1183 list_add_tail(&queue.list, &sma->sem_pending);1184 else1185 list_add(&queue.list, &sma->sem_pending);1186 1187 queue.status = -EINTR;1188 queue.sleeper = current;1189 current->state = TASK_INTERRUPTIBLE;1190 sem_unlock(sma);1191 1192 if (timeout)1193 jiffies_left = schedule_timeout(jiffies_left);1194 else1195 schedule();1196 1197 error = queue.status;1198 while(unlikely(error == IN_WAKEUP)) {1199 cpu_relax();1200 error = queue.status;1201 }1202 1203 if (error != -EINTR) {1204 /* fast path: update_queue already obtained all requested1205 * resources */1206 goto out_free;1207 }1208 1209 sma = sem_lock(ns, semid);1210 if (IS_ERR(sma)) {1211 error = -EIDRM;1212 goto out_free;1213 }1215 /*1216 * If queue.status != -EINTR we are woken up by another process1217 */1218 error = queue.status;1219 if (error != -EINTR) {1220 goto out_unlock_free;1221 }1222 1223 /*1224 * If an interrupt occurred we have to clean up the queue1225 */1226 if (timeout && jiffies_left == 0)1227 error = -EAGAIN;1228 list_del(&queue.list);1229 1230 out_unlock_free:1231 sem_unlock(sma);1232 out_free:1233 if(sops != fast_sops)1234 kfree(sops);1235 return error;1236 }
1.参数检查,并将参数从用户态地址空间复制到内核地址空间(以免用户态内存swap出去后产生的缺页异常)
2.取undo结构
3.权限及安全性校验
4.原子操作。唤醒其它被阻塞进程或自己被阻塞
5.调度其它进程执行,根据timeout设置超时时钟
6.被唤醒的后处理
IV.UNDO
i.get_undo_list
每个进程都有一个undo_list,用于记录进程修改的信号量值(可以有多个信号量),在进程退出时将修改的信号量值归还给信号量。
get_undo_list取进程的undo_list,如果不存在就分配一个undo_list
937 /* If the task doesn't already have a undo_list, then allocate one 938 * here. We guarantee there is only one thread using this undo list, 939 * and current is THE ONE 940 * 941 * If this allocation and assignment succeeds, but later 942 * portions of this code fail, there is no need to free the sem_undo_list. 943 * Just let it stay associated with the task, and it'll be freed later 944 * at exit time. 945 * 946 * This can block, so callers must hold no locks. 947 */ 948 static inline int get_undo_list(struct sem_undo_list **undo_listp) 949 { 950 struct sem_undo_list *undo_list; 951 952 undo_list = current->sysvsem.undo_list; 953 if (!undo_list) { 954 undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL); 955 if (undo_list == NULL) 956 return -ENOMEM; 957 spin_lock_init(&undo_list->lock); 958 atomic_set(&undo_list->refcnt, 1); 959 INIT_LIST_HEAD(&undo_list->list_proc); 960 961 current->sysvsem.undo_list = undo_list; 962 } 963 *undo_listp = undo_list; 964 return 0; 965 }
ii.lookup_undo
取出进程某个信号量的undo结构
967 static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) 968 { 969 struct sem_undo *walk; 970 971 list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) { 972 if (walk->semid == semid) 973 return walk; 974 } 975 return NULL; 976 }
iii.find_alloc_undo
用于查找当前进程的某信号量的undo,如果存在则返回undo;如果不存在则分配一个undo,并将undo分别添加到进程undo链表、信号量undo链表中,并返回undo
978 /** 979 * find_alloc_undo - Lookup (and if not present create) undo array 980 * @ns: namespace 981 * @semid: semaphore array id 982 * 983 * The function looks up (and if not present creates) the undo structure. 984 * The size of the undo structure depends on the size of the semaphore 985 * array, thus the alloc path is not that straightforward. 986 * Lifetime-rules: sem_undo is rcu-protected, on success, the function 987 * performs a rcu_read_lock(). 988 */ 989 static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) 990 { 991 struct sem_array *sma; 992 struct sem_undo_list *ulp; 993 struct sem_undo *un, *new; 994 int nsems; 995 int error; 996 997 error = get_undo_list(&ulp); 998 if (error) 999 return ERR_PTR(error);1000 1001 rcu_read_lock();1002 spin_lock(&ulp->lock);1003 un = lookup_undo(ulp, semid);1004 spin_unlock(&ulp->lock);1005 if (likely(un!=NULL))1006 goto out;1007 rcu_read_unlock();1008 1009 /* no undo structure around - allocate one. */1010 /* step 1: figure out the size of the semaphore array */1011 sma = sem_lock_check(ns, semid);1012 if (IS_ERR(sma))1013 return ERR_PTR(PTR_ERR(sma));1014 1015 nsems = sma->sem_nsems;1016 sem_getref_and_unlock(sma);1017 1018 /* step 2: allocate new undo structure */1019 new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);1020 if (!new) {1021 sem_putref(sma);1022 return ERR_PTR(-ENOMEM);1023 }1024 1025 /* step 3: Acquire the lock on semaphore array */1026 sem_lock_and_putref(sma);1027 if (sma->sem_perm.deleted) {1028 sem_unlock(sma);1029 kfree(new);1030 un = ERR_PTR(-EIDRM);1031 goto out;1032 }1033 spin_lock(&ulp->lock);1034 1035 /*1036 * step 4: check for races: did someone else allocate the undo struct?1037 */1038 un = lookup_undo(ulp, semid);1039 if (un) {1040 kfree(new);1041 goto success;1042 }1043 /* step 5: initialize & link new undo structure */1044 new->semadj = (short *) &new[1];1045 new->ulp = ulp;1046 new->semid = semid;1047 assert_spin_locked(&ulp->lock);1048 list_add_rcu(&new->list_proc, &ulp->list_proc);1049 assert_spin_locked(&sma->sem_perm.lock);1050 list_add(&new->list_id, &sma->list_id);1051 un = new;1052 1053 success:1054 spin_unlock(&ulp->lock);1055 rcu_read_lock();1056 sem_unlock(sma);1057 out:1058 return un;1059 }
iv.exit_sem
进程在退出时(正常退出或异常退出),会将SEM_UNDO标识的操作的信号量值返还给信号量,归还信号量后会尝试唤醒被阻塞进程。
正常退出:exit->do_exit->exit_sem
异常退出:do_signal->get_signal_to_deliver->do_group_exit->do_exit->exit_sem(进程异常退出时,内核会发送相应异常信号给进程,如SIGHUP、SIGSEGV、SIGILL)
exit_sem用于将undo中的信号量值归还给信号量
1266 * add semadj values to semaphores, free undo structures.1267 * undo structures are not freed when semaphore arrays are destroyed1268 * so some of them may be out of date.1269 * IMPLEMENTATION NOTE: There is some confusion over whether the1270 * set of adjustments that needs to be done should be done in an atomic1271 * manner or not. That is, if we are attempting to decrement the semval1272 * should we queue up and wait until we can do so legally?1273 * The original implementation attempted to do this (queue and wait).1274 * The current implementation does not do so. The POSIX standard1275 * and SVID should be consulted to determine what behavior is mandated.1276 */1277 void exit_sem(struct task_struct *tsk)1278 {1279 struct sem_undo_list *ulp;1280 1281 ulp = tsk->sysvsem.undo_list;1282 if (!ulp)1283 return;1284 tsk->sysvsem.undo_list = NULL;1285 1286 if (!atomic_dec_and_test(&ulp->refcnt))1287 return;1288 1289 for (;;) {1290 struct sem_array *sma;1291 struct sem_undo *un;1292 int semid;1293 int i;1294 1295 rcu_read_lock();1296 un = list_entry_rcu(ulp->list_proc.next,1297 struct sem_undo, list_proc);1298 if (&un->list_proc == &ulp->list_proc)1299 semid = -1;1300 else1301 semid = un->semid;1302 rcu_read_unlock();1303 1304 if (semid == -1)1305 break;1306 1307 sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid);1308 1309 /* exit_sem raced with IPC_RMID, nothing to do */1310 if (IS_ERR(sma))1311 continue;1312 1313 un = lookup_undo(ulp, semid);1314 if (un == NULL) {1315 /* exit_sem raced with IPC_RMID+semget() that created1316 * exactly the same semid. Nothing to do.1317 */1318 sem_unlock(sma);1319 continue;1320 }1321 1322 /* remove un from the linked lists */1323 assert_spin_locked(&sma->sem_perm.lock);1324 list_del(&un->list_id);1325 1326 spin_lock(&ulp->lock);1327 list_del_rcu(&un->list_proc);1328 spin_unlock(&ulp->lock);1329 1330 /* perform adjustments registered in un */1331 for (i = 0; i < sma->sem_nsems; i++) {1332 struct sem * semaphore = &sma->sem_base[i];1333 if (un->semadj[i]) {1334 semaphore->semval += un->semadj[i];1335 /*1336 * Range checks of the new semaphore value,1337 * not defined by sus:1338 * - Some unices ignore the undo entirely1339 * (e.g. HP UX 11i 11.22, Tru64 V5.1)1340 * - some cap the value (e.g. FreeBSD caps1341 * at 0, but doesn't enforce SEMVMX)1342 *1343 * Linux caps the semaphore value, both at 01344 * and at SEMVMX.1345 *1346 * Manfred <manfred@colorfullife.com>1347 */1348 if (semaphore->semval < 0)1349 semaphore->semval = 0;1350 if (semaphore->semval > SEMVMX)1351 semaphore->semval = SEMVMX;1352 semaphore->sempid = task_tgid_vnr(current);1353 }1354 }1355 sma->sem_otime = get_seconds();1356 /* maybe some queued-up processes were waiting for this */1357 update_queue(sma);1352 semaphore->sempid = task_tgid_vnr(current);1353 }1354 }1355 sma->sem_otime = get_seconds();1356 /* maybe some queued-up processes were waiting for this */1357 update_queue(sma);1358 sem_unlock(sma);1359 1360 call_rcu(&un->rcu, free_un);1361 }1362 kfree(ulp);1363 }
V.信号量移除
信号量不在使用时,会被移除;
semctl->semctl_down->freeary
514 /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked 515 * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex 516 * remains locked on exit. 517 */ 518 static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 519 { 520 struct sem_undo *un, *tu; 521 struct sem_queue *q, *tq; 522 struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 523 524 /* Free the existing undo structures for this semaphore set. */ 525 assert_spin_locked(&sma->sem_perm.lock); 526 list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { 527 list_del(&un->list_id); 528 spin_lock(&un->ulp->lock); 529 un->semid = -1; 530 list_del_rcu(&un->list_proc); 531 spin_unlock(&un->ulp->lock); 532 call_rcu(&un->rcu, free_un); 533 } 534 535 /* Wake up all pending processes and let them fail with EIDRM. */ 536 list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { 537 list_del(&q->list); 538 539 q->status = IN_WAKEUP; 540 wake_up_process(q->sleeper); /* doesn't sleep */ 541 smp_wmb(); 542 q->status = -EIDRM; /* hands-off q */ 543 } 544 545 /* Remove the semaphore set from the IDR */ 546 sem_rmid(ns, sma); 547 sem_unlock(sma); 548 549 ns->used_sems -= sma->sem_nsems; 550 security_sem_free(sma); 551 ipc_rcu_putref(sma); 552 }
1.将undo从信号量及进程链表中移除,并释放undo结构内存
2.唤醒被阻塞进程,通知信号量已经移除
3.从信号量基数树中移除信号量
4.将使用信号量个数返还给信号量可使用个数空间
5.释放信号量结构内存
- linux SysV IPC sem信号量实现
- linux SysV IPC实现
- IPC-sem 信号量
- IPC信号量 sem
- linux SysV IPC shm共享内存实现
- linux SysV IPC msg消息队列实现
- IPC实现机制(四)---信号量(sem)
- linux sem信号量使用
- Linux sem信号量使用
- sem信号量
- sem 信号量
- 信号量sem
- Linux进程间通讯之信号量sem
- Linux线程同步机制四--信号量sem
- Linux-IPC之信号量
- linux IPC--信号量
- linux IPC--信号量
- Android 不支持 SYSV IPC (SYSV IPC)
- 二、SQL语句映射文件(1)resultMap
- 总结:复合数据对象
- eclipse常用快捷键
- 总结:程序设计基础
- java连接MySql数据库
- linux SysV IPC sem信号量实现
- 用伪类实现百度的评价星星的功能
- 二、SQL语句映射文件(2)增删改查、参数、缓存
- 电脑flash player 安装时提示activex无法注册
- 验证码识别技术的难易程度
- Android Studio设置
- 验证码识别系统
- nyoj 21 三个水杯
- STL 目录