Linux系统中进程的状态
Linux系统中进程存在R、S、D、T、Z、X六种状态。
D Uninterruptible sleep (usually IO)
R Running or runnable (on run queue)
S Interruptible sleep (waiting for an event to complete)
T Stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z Defunct ("zombie") process, terminated but not
reaped by its parent.
其中,主要需要了解的是D(不可中断的睡眠状态)、S(可中断的睡眠状态)、R(运行状态)、Z(僵尸进程)。
进程的运行状态自然没有什么好多说的,进程在cpu的运行队列上,可以被cpu调度到。
D和S两种状态有什么区别
TASK_INTERRUPTIBLE是可以被信号和wake_up()唤醒的,当信号到来时,进程会被设置为可运行。而TASK_UNINTERRUPTIBLE只能被wake_up()唤醒。 信号是在软件层次上对中断机制的一种模拟,软中断。
信号事件的发生有两个来源:
硬件来源:(比如我们按下了键盘或者其它硬件故障);
软件来源:最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。
可中断的睡眠状态和不可中断的睡眠状态。处于睡眠状态的进程不会被调度到CPU进行执行,而是否可中断的意思是指进程是否会响应异步信号,如果是可中断的,当进程收到某个信 号时其会重新回到TASK_RUNNING状态。值得注意的是,如果处于不可中断的睡眠状态时,进程将不响应异步信号,比如你无法“kill -9”
设置TASK_INTERRUPTIBLE状态的一个例子
/* * Read a single request into the userspace filesystem's buffer. This * function waits until a request is available, then removes it from * the pending list and copies request data to userspace buffer. If * no reply is needed (FORGET) or request has been aborted or there * was an error during the copying then it's finished by calling * request_end(). Otherwise add it to the processing list, and set * the 'sent' flag. */ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { int err; struct fuse_req *req; struct fuse_in *in; struct fuse_copy_state cs; unsigned reqsize; struct file *file = iocb->ki_filp; struct fuse_conn *fc = fuse_get_conn(file); if (!fc) return -EPERM; restart: spin_lock(&fc->lock); err = -EAGAIN; if ((file->f_flags & O_NONBLOCK) && fc->connected && !request_pending(fc)) goto err_unlock; request_wait(fc); |
/* Wait until a request is available on the pending list */ static void request_wait(struct fuse_conn *fc) __releases(&fc->lock) __acquires(&fc->lock) { DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&fc->waitq, &wait); while (fc->connected && !request_pending(fc)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) break; spin_unlock(&fc->lock); schedule(); spin_lock(&fc->lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&fc->waitq, &wait); } |
static void end_io_requests(struct fuse_conn *fc) __releases(&fc->lock) __acquires(&fc->lock) { while (!list_empty(&fc->io)) { struct fuse_req *req = list_entry(fc->io.next, struct fuse_req, list); void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; req->aborted = 1; req->out.h.error = -ECONNABORTED; req->state = FUSE_REQ_FINISHED; list_del_init(&req->list); wake_up(&req->waitq); if (end) { req->end = NULL; __fuse_get_request(req); spin_unlock(&fc->lock); wait_event(req->waitq, !req->locked); end(fc, req); fuse_put_request(fc, req); spin_lock(&fc->lock); } } } |
从上面的例子,可以看到内核中关于设置进程为睡眠状态的一些应用场景,这个例子是fuse驱动中的一个处理过程。进程等待一个读请求有效,在等待过程中将进程设置为TASK_INTERRUPTIBLE状态,并调用schedule()调度运行其它进程。当IO完成的时候,IO操作的回调函数wake_up(&req->waitq);唤醒休眠的进程。该处使用TASK_INTERRUPTIBLE主要是防止如果IO操作长时间没有完成,进程长时间休眠而没有办法退出进程。设置成TASK_INTERRUPTIBLE,如果进程长时间等待IO未果,可以通过向进程发送信号,来结束进程休眠在等待IO完成上面。
之后我们再分析一处使用TASK_UNINTERRUPTIBLE的例子
get_registers函数用来获取usb device芯片中的某个寄存器的值,比如某些wifi芯片是usb接口的,在操作这种wifi芯片上的寄存器的时候必须通过usb来通信获取。对于pcie接口的芯片,我们可以认为这个过程就是读写内存一样(pcie空间被映射到了内存地址)。对于usb就不是如此了,这个过程包括usb device向设备发起一个读请求,usb device再回应这个请求,这个过程是需要消耗一定时间的。如下面的函数处理过程,先将发起读请求的进程设置为TASK_UNINTERRUPTIBLE状态,再调用usb_submit_urb发起读请求,最后调用schedule()调度执行其它进程。当这个读请求的IO完成的时候,IO请求的回调函数ctrl_callback中会执行wake_up将之前休眠的进程唤醒。
static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, void *data) { int ret; char *buffer; DECLARE_WAITQUEUE(wait, current); buffer = kmalloc(size, GFP_KERNEL); if (!buffer) { if (netif_msg_drv(pegasus)) dev_warn(&pegasus->intf->dev, "out of memory in %s\n", __func__); return -ENOMEM; } add_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); while (pegasus->flags & ETH_REGS_CHANGED) schedule(); remove_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_RUNNING); pegasus->dr.bRequestType = PEGASUS_REQT_READ; pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; pegasus->dr.wValue = cpu_to_le16(0); pegasus->dr.wIndex = cpu_to_le16(indx); pegasus->dr.wLength = cpu_to_le16(size); pegasus->ctrl_urb->transfer_buffer_length = size; usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0), (char *) &pegasus->dr, buffer, size, ctrl_callback, pegasus); add_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); /* using ATOMIC, we'd never wake up if we slept */ if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { set_current_state(TASK_RUNNING); if (ret == -ENODEV) netif_device_detach(pegasus->net); if (netif_msg_drv(pegasus) && printk_ratelimit()) dev_err(&pegasus->intf->dev, "%s, status %d\n", __func__, ret); goto out; } schedule(); out: remove_wait_queue(&pegasus->ctrl_wait, &wait); memcpy(data, buffer, size); kfree(buffer); return ret; } |
static void ctrl_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; int status = urb->status; if (!pegasus) return; switch (status) { case 0: if (pegasus->flags & ETH_REGS_CHANGE) { pegasus->flags &= ~ETH_REGS_CHANGE; pegasus->flags |= ETH_REGS_CHANGED; update_eth_regs_async(pegasus); return; } break; case -EINPROGRESS: return; case -ENOENT: break; default: if (netif_msg_drv(pegasus) && printk_ratelimit()) dev_dbg(&pegasus->intf->dev, "%s, status %d\n", __func__, status); } pegasus->flags &= ~ETH_REGS_CHANGED; wake_up(&pegasus->ctrl_wait); } |