linux进程内核资源回收
转载自http://blog.sina.com.cn/s/blog_605507340101dcw6.htmllinux进程退出后操作系统是如何删除这个进程对应的内核资源的进程退出,大概可以分为三种方式:运行完后正常退出,发生某种异常如访问非法内存,除零等的异常退出,被kill掉而退出的。作为程序的main,它的原型应该是:int main(int argc, char a
·
转载自http://blog.sina.com.cn/s/blog_605507340101dcw6.html
linux进程退出后操作系统是如何删除这个进程对应的内核资源的
进程退出,大概可以分为三种方式:运行完后正常退出,发生某种异常如访问非法内存,除零等的异常退出,被kill掉而退出的。作为程序的main,它的原型应该是:
int main(int argc, char argv[]),虽然以前也能用void main(int argc, char argv[]) ,如在VC6中可以编译通过,但是在新一些的编译器如gcc3.2和g++中,要么是会产生警告,要么就是编译通不过。这是因为新的标准规定了main函数应该返回整型数。在正常的编译时,编译器都会在编出的可执行文件main函数的后面加上exit函数。而当进程发生异常时,回调用abort函数,这两个都是glibc的函数,而当使用kill命令等方式终止进程的运行时,操作系统linux会调用do_signal内核函数,调用关系如下所示:
完成这个进程在内核中资源的回收就是由这个do_exit函数完成的。下面我们来详细看一下这个函数。这个函数分别调用了exit_mm, exit_sem, __exit_files, __exit_fs, exit_namespace, exit_thread, cpuset_exitexit_keys等等,其中__exit_files函数所完成的工作就是回收这个进程文件系统相关的资源,因为在linux系统中,一个进程所打开的所有的文件包含各种设备,socket等都是用文件描述符来对应,下面我们就详细看一下这个函数。首先看一下这个函数的详细调用关系,如下所示:
到这个时候,则就要依据这个文件句柄的具体是啥来决定调用哪个函数。下面以socket句柄为例来做介绍。
Linux文件系统中定义的文件操作数据结构 (fs.h)如下:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
};
在socket.c文件中定义的对应socket的文件操作的这个结构值如下:
static struct file_operations socket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.aio_read = sock_aio_read,
.aio_write = sock_aio_write,
.poll = sock_poll,
.unlocked_ioctl = sock_ioctl,
.mmap = sock_mmap,
.open = sock_no_open,
.release = sock_close,
.fasync = sock_fasync,
.readv = sock_readv,
.writev = sock_writev,
.sendpage = sock_sendpage
};
从中可以看出并不是这个文件操作结构中的每一个子变量在socket中都有定义的。当通过socket函数调用,其调用关系如下所示:
由于socket本身也是一个通用的架构,可以支持各种各样的通讯协议,第一个函数sock_create则是为socket本身的通用架构服务的,并会调用到相应的协议定义的socket创建函数。而第二函数sock_map_fd则是构建一个从socket到文件句柄的一个映射,其函数原型如下:int sock_map_fd(struct socket *sock)。这个函数里的如下这个行代码就是把socket定义的文件操作赋给这个映射的文件句柄中去:file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;
这样如果进程创建了socket的话,当它退出的时候,它就会调用socket所对应的sock_close来释放socket所占有的内核资源。以前面讨论的TIPC协议,当一个应用程序创建了tipc的socket,当这个应用程序的进程被kill掉的情况下,其调用关系如下:
这样这个进程中在内核中创建 tipc 协议通讯使用内核资源就都被删除,如 tipc 的 port 等等。
更多推荐
已为社区贡献2条内容
所有评论(0)