Linux内核情景分析之消息队列
早期的Unix通信只有管道与信号,管道的缺点:
int ipc(unsigned int call,int firtst,int second,int third,void*ptr,int firth);
#define SEMOP 1#define SEMGET 2#define SEMCTL 3#define MSGSND 11#define MSGRCV 12#define MSGGET 13#define MSGCTL 14#define SHMAT 21#define SHMDT 22#define SHMGET 23#define SHMCTL 24
struct ipc_ids {int size;int in_use;int max_id;unsigned short seq;unsigned short seq_max;struct semaphore sem;spinlock_t ary;struct ipc_id* entries;//指向一个结构数组};
struct ipc_id {struct kern_ipc_perm* p;};
/* used by in-kernel data structures */struct kern_ipc_perm{key_t key;//建值uid_t uid;gid_t gid;uid_t cuid;gid_t cgid;mode_t mode;unsigned long seq;};
/* one msq_queue structure for each present queue on the system */struct msg_queue {struct kern_ipc_perm q_perm;time_t q_stime; /* last msgsnd time */time_t q_rtime; /* last msgrcv time */time_t q_ctime; /* last change time */unsigned long q_cbytes; /* current number of bytes on queue */unsigned long q_qnum; /* number of messages in queue */unsigned long q_qbytes; /* max number of bytes on queue */pid_t q_lspid; /* pid of last msgsnd */pid_t q_lrpid; /* last receive pid */struct list_head q_messages;struct list_head q_receivers;struct list_head q_senders;};

//可以用于2个目的,通过给定的key创建队列,通过给定的key查找已存在队列asmlinkage long sys_msgget (key_t key, int msgflg){int id, ret = -EPERM;struct msg_queue *msq;//队列down(&msg_ids.sem);if (key == IPC_PRIVATE) //自己私用,无条件创建一个报文队列ret = newque(key, msgflg);//根据key与msgflgelse if ((id = ipc_findkey(&msg_ids, key)) == -1) { /* key没有找到key not used */if (!(msgflg & IPC_CREAT))//没找到,但没设定IPC_CREAT那就返回错误ret = -ENOENT;else//设定了就创建报文队列ret = newque(key, msgflg);} else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {//同时设定了IPC_CREAT与IPC_EXCL返回错误ret = -EEXIST;} else {msq = msg_lock(id);if(msq==NULL)BUG();if (ipcperms(&msq->q_perm, msgflg))//检查访问权限是否符合规则ret = -EACCES;elseret = msg_buildid(id, msq->q_perm.seq);//将数组下标转换一体化的标识号msg_unlock(id);}up(&msg_ids.sem);return ret;//返回标识号}
static int newque (key_t key, int msgflg){int id;struct msg_queue *msq;//队列头msq = (struct msg_queue *) kmalloc (sizeof (*msq), GFP_KERNEL);//分配结构if (!msq)return -ENOMEM;id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni);//分配一个标识号if(id == -1) {kfree(msq);return -ENOSPC;}//以下是报文队列头各种初始化msq->q_perm.mode = (msgflg & S_IRWXUGO);msq->q_perm.key = key;//key值msq->q_stime = msq->q_rtime = 0;msq->q_ctime = CURRENT_TIME;msq->q_cbytes = msq->q_qnum = 0;msq->q_qbytes = msg_ctlmnb;msq->q_lspid = msq->q_lrpid = 0;INIT_LIST_HEAD(&msq->q_messages);INIT_LIST_HEAD(&msq->q_receivers);INIT_LIST_HEAD(&msq->q_senders);msg_unlock(id);//将标识号转换为一个一体化的标识号,因为实际分配的id实际是数组下标会重复使用return msg_buildid(id,msq->q_perm.seq);- }
/*** ipc_addid - add an IPC identifier* @ids: IPC identifier set* @new: new IPC permission set* @size: new size limit for the id array** Add an entry 'new' to the IPC arrays. The permissions object is* initialised and the first free entry is set up and the id assigned* is returned. The list is returned in a locked state on success.* On failure the list is not locked and -1 is returned.*///全局队列管理结构 新创建的队列头的这个结构int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size){int id;size = grow_ary(ids,size);//增加管理数组的大小for (id = 0; id < size; id++) {if(ids->entries[id].p == NULL)//总根看管理数组的哪个为空goto found;}return -1;found:ids->in_use++;//已使用++if (id > ids->max_id)//最大的key值ids->max_id = id;new->cuid = new->uid = current->euid;//uid赋值new->gid = new->cgid = current->egid;//gid赋值new->seq = ids->seq++;//序列号if(ids->seq > ids->seq_max)//超过了最大的限定ids->seq = 0;//从0开始继续spin_lock(&ids->ary);ids->entries[id].p = new;//挂钩,新创建的报文头与总跟挂钩成功return id;}
/* message buffer for msgsnd and msgrcv calls */struct msgbuf {long mtype; /* type of message */char mtext[1]; /* message text */};
/* one msg_msg structure for each message */struct msg_msg {struct list_head m_list;long m_type;//类型int m_ts; /*长度 message text size */struct msg_msgseg* next;/* the actual message follows immediately */};
struct msg_msgseg {struct msg_msgseg* next;/* the next part of the message follows immediately */};

//标识号 发送格式 //大小 //标志位asmlinkage long sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg){struct msg_queue *msq;//队列头struct msg_msg *msg;//内核保存信息的格式long mtype;int err;//消息不可以超过8kif (msgsz > msg_ctlmax || (long) msgsz < 0 || msqid < 0)return -EINVAL;if (get_user(mtype, &msgp->mtype))//从用户空间拷贝到内核return -EFAULT;if (mtype < 1)return -EINVAL;msg = load_msg(msgp->mtext, msgsz);//分配缓冲区保存消息(从用户拷贝到内核)if(IS_ERR(msg))return PTR_ERR(msg);msg->m_type = mtype;//消息类型msg->m_ts = msgsz;//消息大小msq = msg_lock(msqid);//根据给定的标号msg_msg找到相应的报文队列,将其数据结构上锁err=-EINVAL;if(msq==NULL)goto out_free;retry:err= -EIDRM;if (msg_checkid(msq,msqid))//验证下id号goto out_unlock_free;err=-EACCES;if (ipcperms(&msq->q_perm, S_IWUGO)) //检查是否有权限向这个队列发送报文goto out_unlock_free;//当前报文大小+当前队列统计的字节数超过了报文队列的总容量.或者报文的个数超过了限制,那就不可以发送了if(msgsz + msq->q_cbytes > msq->q_qbytes ||1 + msq->q_qnum > msq->q_qbytes) {struct msg_sender s;if(msgflg&IPC_NOWAIT) {//是否等待,不等待直接退出err=-EAGAIN;goto out_unlock_free;}ss_add(msq, &s);//挂载到报文队列q_sender链,这样可以通过此链找到休眠正在等待发送的进程msg_unlock(msqid);schedule();//调度current->state= TASK_RUNNING;msq = msg_lock(msqid);err = -EIDRM;if(msq==NULL)goto out_free;ss_del(&s);//删除if (signal_pending(current)) {err=-EINTR;goto out_unlock_free;}goto retry;//重新运行一遍}if(!pipelined_send(msq,msg)) {//如果有相关进程正在读这个报文就不用放入队列了/* noone is waiting for this message, enqueue it */list_add_tail(&msg->m_list,&msq->q_messages);//链入队列msq->q_cbytes += msgsz;//总数++msq->q_qnum++;//数目++atomic_add(msgsz,&msg_bytes);atomic_inc(&msg_hdrs);}err = 0;msg = NULL;msq->q_lspid = current->pid;msq->q_stime = CURRENT_TIME;out_unlock_free:msg_unlock(msqid);out_free:if(msg!=NULL)free_msg(msg);return err;}
//报文的源static struct msg_msg* load_msg(void* src, int len){struct msg_msg* msg;struct msg_msgseg** pseg;int err;int alen;alen = len;if(alen > DATALEN_MSG)//一页减去msg结构大小alen = DATALEN_MSG;msg = (struct msg_msg *) kmalloc (sizeof(*msg) + alen, GFP_KERNEL);//一个报文的头if(msg==NULL)return ERR_PTR(-ENOMEM);msg->next = NULL;if (copy_from_user(msg+1, src, alen)) {//从msg的尾部开始拷贝err = -EFAULT;goto out_err;}len -= alen;//剩余长度src = ((char*)src)+alen;//剩余源头pseg = &msg->next;//指向下一个页while(len > 0) {//还有剩余长度struct msg_msgseg* seg;alen = len;if(alen > DATALEN_SEG)//是否超过page-msgsegalen = DATALEN_SEG;seg = (struct msg_msgseg *) kmalloc (sizeof(*seg) + alen, GFP_KERNEL);//获取一页if(seg==NULL) {err=-ENOMEM;goto out_err;}*pseg = seg;//链接seg->next = NULL;if(copy_from_user (seg+1, src, alen)) {//继续拷贝err = -EFAULT;goto out_err;}pseg = &seg->next;len -= alen;src = ((char*)src)+alen;}return msg;out_err:free_msg(msg);return ERR_PTR(err);}
/* one msg_sender for each sleeping sender */struct msg_sender {struct list_head list;struct task_struct* tsk;};
static inline void ss_add(struct msg_queue* msq, struct msg_sender* mss){mss->tsk=current;//获取当前队列的进程运行的进程current->state=TASK_INTERRUPTIBLE;//设置为可中断睡眠状态list_add_tail(&mss->list,&msq->q_senders);//添加到队尾}
int inline pipelined_send(struct msg_queue* msq, struct msg_msg* msg){struct list_head* tmp;tmp = msq->q_receivers.next;//聚集正在睡眠等待接收的读进程while (tmp != &msq->q_receivers) {//表示有struct msg_receiver* msr;msr = list_entry(tmp,struct msg_receiver,r_list);tmp = tmp->next;if(testmsg(msg,msr->r_msgtype,msr->r_mode)) {//类型是否匹配list_del(&msr->r_list);if(msr->r_maxsize < msg->m_ts) {//读的缓冲区是否够用msr->r_msg = ERR_PTR(-E2BIG);wake_up_process(msr->r_tsk);//不够用则将进程唤醒,让其出错返回} else {msr->r_msg = msg;//有的话,直接读取msq->q_lspid = msr->r_tsk->pid;msq->q_rtime = CURRENT_TIME;wake_up_process(msr->r_tsk);return 1;}}}return 0;}
//标识号 用户空间缓冲区 大小asmlinkage long sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,long msgtyp, int msgflg)//msgtyp消息类型,{struct msg_queue *msq;//队列头struct msg_receiver msr_d;//接收的需要睡眠的进程struct list_head* tmp;struct msg_msg* msg, *found_msg;int err;int mode;if (msqid < 0 || (long) msgsz < 0)return -EINVAL;mode = convert_mode(&msgtyp,msgflg);msq = msg_lock(msqid);//根据报文队列标识号,找到具体队列if(msq==NULL)return -EINVAL;retry:err=-EACCES;if (ipcperms (&msq->q_perm, S_IRUGO))//检测是否具有权限goto out_unlock;tmp = msq->q_messages.next;found_msg=NULL;while (tmp != &msq->q_messages) {//遍历完msg = list_entry(tmp,struct msg_msg,m_list);//根据队列当前项找到其指针if(testmsg(msg,msgtyp,mode)) {found_msg = msg;//查找到了消息if(mode == SEARCH_LESSEQUAL && msg->m_type != 1) {found_msg=msg;msgtyp=msg->m_type-1;//将type减到比这个报文的类型值更小,看能否找到更小的} else {found_msg=msg;break;}}tmp = tmp->next;}if(found_msg) {msg=found_msg;if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {//如果接收大小小于报文大小,出错err=-E2BIG;goto out_unlock;}list_del(&msg->m_list);//否则将该报文从队列脱链msq->q_qnum--;msq->q_rtime = CURRENT_TIME;msq->q_lrpid = current->pid;msq->q_cbytes -= msg->m_ts;atomic_sub(msg->m_ts,&msg_bytes);atomic_dec(&msg_hdrs);ss_wakeup(&msq->q_senders,0);//将发送的睡眠等待进程全部唤醒,因为拿出了一个报文msg_unlock(msqid);out_success:msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz;if (put_user (msg->m_type, &msgp->mtype) ||//实际接收的报文类型,通过put_user送回用户空间store_msg(msgp->mtext, msg, msgsz)) {//将实际接收到的复制到用户空间msgsz = -EFAULT;}free_msg(msg);//释放内核缓存return msgsz;} else//报文队列还没有报文可供接收{struct msg_queue *t;/* no message waiting. Prepare for pipelined* receive.*/if (msgflg & IPC_NOWAIT) {//不等待直接返回错误err=-ENOMSG;goto out_unlock;}list_add_tail(&msr_d.r_list,&msq->q_receivers);//链入等待接收队列进程msr_d.r_tsk = current;//保存各种信息msr_d.r_msgtype = msgtyp;msr_d.r_mode = mode;if(msgflg & MSG_NOERROR)msr_d.r_maxsize = INT_MAX;elsemsr_d.r_maxsize = msgsz;msr_d.r_msg = ERR_PTR(-EAGAIN);current->state = TASK_INTERRUPTIBLE;msg_unlock(msqid);//解锁并调度//当前进程一旦睡下,以下需要等待进程通过pipelined_send()向其发送报文,并且选择这个进程作为接收进程才会被唤醒schedule();current->state = TASK_RUNNING;msg = (struct msg_msg*) msr_d.r_msg;if(!IS_ERR(msg)) //表示已经成功接收goto out_success;//以下是因为缓冲区太小,唤醒了睡眠进程依旧无法接收,而是被信号唤醒的错误处理t = msg_lock(msqid);//对报文加锁,隐藏着等待,可能被其他进程抢先锁住该队列if(t==NULL)msqid=-1;msg = (struct msg_msg*)msr_d.r_msg;if(!IS_ERR(msg)) {//在锁住队列之前,还有可能接收到其他进程pipelined_send发来的报文/* our message arived while we waited for* the spinlock. Process it.*///所以还需要检查下是否成功接收到报文if(msqid!=-1)msg_unlock(msqid);goto out_success;}err = PTR_ERR(msg);if(err == -EAGAIN) {//要将本进程的msg_receiver结构拖链,并且看是否有信号处理if(msqid==-1)BUG();list_del(&msr_d.r_list);if (signal_pending(current))//如果没有信号处理,则跳转到retry重新开始err=-EINTR;elsegoto retry;}}out_unlock:if(msqid!=-1)msg_unlock(msqid);return err;}
long sys_msgctl(int msqid,int cmd,struct msqid_ds*buf);
/** Control commands used with semctl, msgctl and shmctl* see also specific commands in sem.h, msg.h and shm.h*/#define IPC_RMID 0 /* remove resource关闭报文队列 */#define IPC_SET 1 /* set ipc_perm options 改变ipc设施的相关状态与属性*/#define IPC_STAT 2 /* get ipc_perm options 获取状态*/#define IPC_INFO 3 /* see ipcs 统计信息*/
/* ipcs ctl commands */#define MSG_STAT 11#define MSG_INFO 12
/* Obsolete, used only for backwards compatibility and libc5 compiles */struct msqid_ds {struct ipc_perm msg_perm;struct msg *msg_first; /* first message on queue,unused */struct msg *msg_last; /* last message in queue,unused */__kernel_time_t msg_stime; /* last msgsnd time */__kernel_time_t msg_rtime; /* last msgrcv time */__kernel_time_t msg_ctime; /* last change time */unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */unsigned long msg_lqbytes; /* ditto */unsigned short msg_cbytes; /* current number of bytes on queue */unsigned short msg_qnum; /* number of messages in queue */unsigned short msg_qbytes; /* max number of bytes on queue */__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */__kernel_ipc_pid_t msg_lrpid; /* last receive pid */};
/* buffer for msgctl calls IPC_INFO, MSG_INFO */struct msginfo {int msgpool;int msgmap;int msgmax;int msgmnb;int msgmni;int msgssz;int msgtql;unsigned short msgseg;};
asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds *buf){int err, version;struct msg_queue *msq;struct msq_setbuf setbuf;struct kern_ipc_perm *ipcp;if (msqid < 0 || cmd < 0)return -EINVAL;version = ipc_parse_version(&cmd);//判断是64位版本还是32位版本switch (cmd) {//根据不同类型选择不同操作case IPC_INFO: //合在一起case MSG_INFO:{struct msginfo msginfo;int max_id;if (!buf)return -EFAULT;/* We must not return kernel stack data.* due to padding, it's not enough* to set all member fields.*/memset(&msginfo,0,sizeof(msginfo));msginfo.msgmni = msg_ctlmni;msginfo.msgmax = msg_ctlmax;msginfo.msgmnb = msg_ctlmnb;msginfo.msgssz = MSGSSZ;msginfo.msgseg = MSGSEG;down(&msg_ids.sem);if (cmd == MSG_INFO) {msginfo.msgpool = msg_ids.in_use;msginfo.msgmap = atomic_read(&msg_hdrs);msginfo.msgtql = atomic_read(&msg_bytes);} else {msginfo.msgmap = MSGMAP;msginfo.msgpool = MSGPOOL;msginfo.msgtql = MSGTQL;}max_id = msg_ids.max_id;up(&msg_ids.sem);if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))//从内核拷贝到用户空间return -EFAULT;return (max_id < 0) ? 0: max_id;}case MSG_STAT://stat状态相关操作case IPC_STAT:{struct msqid64_ds tbuf;int success_return;if (!buf)return -EFAULT;if(cmd == MSG_STAT && msqid > msg_ids.size)return -EINVAL;memset(&tbuf,0,sizeof(tbuf));msq = msg_lock(msqid);if (msq == NULL)return -EINVAL;if(cmd == MSG_STAT) {success_return = msg_buildid(msqid, msq->q_perm.seq);} else {err = -EIDRM;if (msg_checkid(msq,msqid))//id检查goto out_unlock;success_return = 0;}err = -EACCES;if (ipcperms (&msq->q_perm, S_IRUGO))//权限判断goto out_unlock;kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);tbuf.msg_stime = msq->q_stime;tbuf.msg_rtime = msq->q_rtime;tbuf.msg_ctime = msq->q_ctime;tbuf.msg_cbytes = msq->q_cbytes;tbuf.msg_qnum = msq->q_qnum;tbuf.msg_qbytes = msq->q_qbytes;tbuf.msg_lspid = msq->q_lspid;tbuf.msg_lrpid = msq->q_lrpid;msg_unlock(msqid);if (copy_msqid_to_user(buf, &tbuf, version))//从内核拷贝到用户return -EFAULT;return success_return;}case IPC_SET://set命令操作if (!buf)return -EFAULT;if (copy_msqid_from_user (&setbuf, buf, version))//从用户拷贝到内核return -EFAULT;break;case IPC_RMID:break;default:return -EINVAL;}down(&msg_ids.sem);msq = msg_lock(msqid);err=-EINVAL;if (msq == NULL)goto out_up;err = -EIDRM;if (msg_checkid(msq,msqid))goto out_unlock_up;ipcp = &msq->q_perm;err = -EPERM;if (current->euid != ipcp->cuid &¤t->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))//是否有权限操作/* We _could_ check for CAP_CHOWN above, but we don't */goto out_unlock_up;switch (cmd) {case IPC_SET:{if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))goto out_unlock_up;msq->q_qbytes = setbuf.qbytes;ipcp->uid = setbuf.uid;ipcp->gid = setbuf.gid;ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |(S_IRWXUGO & setbuf.mode);msq->q_ctime = CURRENT_TIME;/* sleeping receivers might be excluded by* stricter permissions.*/expunge_all(msq,-EAGAIN);//使所有正在等待此队列接收报文的进程都出错返回/* sleeping senders might be able to send* due to a larger queue size.*/ss_wakeup(&msq->q_senders,0);//将所有正在等待此队列发送报文的进程都唤醒,进行新一轮尝试msg_unlock(msqid);break;}case IPC_RMID:freeque (msqid); //将所有正在等待的发送还是接收的进程全部唤醒,让其出错返回break;}err = 0;out_up:up(&msg_ids.sem);return err;out_unlock_up:msg_unlock(msqid);goto out_up;out_unlock:msg_unlock(msqid);return err;}
static void freeque (int id){struct msg_queue *msq;struct list_head *tmp;msq = msg_rmid(id);expunge_all(msq,-EIDRM);//将所有读写的从链表拖链,让其出错返回ss_wakeup(&msq->q_senders,1);//唤醒msg_unlock(id);tmp = msq->q_messages.next;while(tmp != &msq->q_messages) {//将所有报文释放struct msg_msg* msg = list_entry(tmp,struct msg_msg,m_list);tmp = tmp->next;atomic_dec(&msg_hdrs);free_msg(msg);}atomic_sub(msq->q_cbytes, &msg_bytes);kfree(msq);//是否队列头}
Linux内核情景分析之消息队列的更多相关文章
- linux内核情景分析之execve()
用来描述用户态的cpu寄存器在内核栈中保存情况.可以获取用户空间的信息 struct pt_regs { long ebx; //可执行文件路径的指针(regs.ebx中 long ecx; //命令 ...
- [8]windows内核情景分析--窗口消息
消息与钩子 众所周知,Windows系统是消息驱动的,现在我们就来看Windows的消息机制. 早期的Windows的窗口图形机制是在用户空间实现的,后来为了提高图形处理效率,将这部分移入内核空间,在 ...
- Linux内核情景分析的alloc_pages
NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCON ...
- linux内核情景分析之exit与Wait
//第一层系统调用 asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } 其主体是 ...
- linux内核情景分析之内核中的互斥操作
信号量机制: struct sempahore是其结构,定义如下 struct semaphore { atomic_t count;//资源数目 int sleepers;//等待进程数目 wait ...
- linux内核情景分析之信号实现
信号在进程间通信是异步的,每个进程的task_struct结构有一个sig指针,指向一个signal_struct结构 定义如下 struct signal_struct { atomic_t cou ...
- Linux内核情景分析之异常访问,用户堆栈的扩展
情景假设: 在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存.也就是所说的野指针访问. 当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况 1.如果 ...
- linux内核情景分析之强制性调度
从系统调用返回到用户空间是否调度,从ret_with_reschedule可看出,是否真正调度,取决于当前进程的pcb中的need_resched是否设置为1,那如何设置为1取决于以下几种情况: 时间 ...
- linux内核情景分析之匿名管道
管道的机制由pipe()创建,由pipe()所建立的管道两端都在同一进程.所以必须在fork的配合下,才可以在具有亲缘关系的进程通信 /* * sys_pipe() is the normal C c ...
随机推荐
- java.lang.ClassCastException: com.sun.proxy.$Proxy53 cannot be cast to cn.service.impl.WorkinggServiceImpl
java.lang.ClassCastException: com.sun.proxy.$Proxy53 cannot be cast to cn.service.impl.WorkinggServi ...
- 3 个用于数据科学的顶级 Python 库
使用这些库把 Python 变成一个科学数据分析和建模工具. Python 的许多特性,比如开发效率.代码可读性.速度等使之成为了数据科学爱好者的首选编程语言.对于想要升级应用程序功能的数据科学家和机 ...
- visio画图ER图表和字段注释
最近年底属于验收的项目很多,大多数写文档中,数据库的设计ER图是比不可少的.下面记一下几个常用的用法.以下用的市visio版本为2007,由于菜单样式新版本可能有所不同,请对照相应功能进行操作! 1. ...
- pandas知识点(汇总和计算描述统计)
调用DataFrame的sum方法会返还一个含有列的Series: In [5]: df = DataFrame([[1.4,np.nan],[7.1,-4.5],[np.nan,np.nan],[0 ...
- 最近使用Nginx的一点新得
1.基本的负载配置 Nginx最简单的配置模块如下 upstream name{ server ip:port; server ip:port; } server { listen 80; serve ...
- F#周报2019年第25期
新闻 Azure Notebook概览 SpecFlow 3就在这里了! 使用新的Try .NET模版创建交互式文档 逐渐演化的.NET Core框架 Dylan与Linebreakers Oslo ...
- JS的跨域理解
前言 周一的学院点开题被批的很惨,换了个校长,各种被抓严,班上已经有两个同学打算休学了.哎,这周的聚会可能是大家集聚的最后一次吧.熬着吧,还是学习我的前端,不管老板学校咋逼了,找个好工作才是王道.今天 ...
- 48、android代码架构总结
之前是按功能模块进行分类,现在随着功能模块越来越多,代码层次不再清晰,所以修改了工程结构: 之前: 经过修改现在: 1.更严谨的遵循mvc架构 bean目录存放的是数据模型 ui存储的是activit ...
- 【Remove Elements】cpp
题目: Given an array and a value, remove all instances of that value in place and return the new lengt ...
- MOTCF 没时间解释了 条件竞争漏洞
moctf 没时间解释了 条件竞争漏洞 题目链接 条件竞争: 在本题目中,上传文件的时候服务器无条件的接收任何类型的文件,但是你上传之后服务器会给你的文件内容修改为too slow. 比如你上传了一句 ...