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.释放信号量结构内存


原创粉丝点击