问题描述
我有一个作业,要求实现一些与文件相关的syscall以及实现它们所需的结构(即文件表和文件句柄)。
这是我对结构的实现:
// File Handle -------------------------
struct filehandle {
int flags;
off_t offset;
unsigned int refcount;
struct vnode *vnode_ptr;
struct lock *offset_lock;
struct spinlock refcount_spinlock;
// flags:
// bit 0: O_RDONLY or O_WRONLY
// bit 1: O_RDWR
// bit 2: is_seekable
};
struct filehandle *filehandle_create(void);
void filehandle_destroy(struct filehandle *);
void filehandle_incref(struct filehandle *);
void filehandle_decref(struct filehandle *);
// File Table --------------------------
struct filetable {
struct filehandle **table;
struct lock **fh_locks;
struct rwlock *table_lock;
int search_start_index;
int capacity;
};
struct filetable *filetable_create(void);
void filetable_destroy(struct filetable *);
void filetable_incref_all(struct filetable *);
void filetable_decref_all(struct filetable *);
int get_min_available_fd(struct filetable *,int *);
void reclaim_fd(struct filetable *,int);
void filetable_lock_handle(struct filetable *,int);
void filetable_unlock_handle(struct filetable *,int);
void filetable_lock(struct filetable *);
void filetable_unlock(struct filetable *);
在结构filehandle
中,我有一个用于更改引用计数的自旋锁。但是我相信这里有问题。
这是sys_close
系统调用的实现:
void sys_close_nocheck(int fd)
{
struct filehandle **handles = curproc->ft->table;
filehandle_decref(handles[fd]);
// Todo accessing the refcount
// without acquiring the lock.
if (handles[fd]->refcount == 0) {
filehandle_destroy(handles[fd]);
}
handles[fd] = NULL;
}
int sys_close(int fd)
{
// when to return EIO?..
struct filetable *ft = curproc->ft;
if (fd < 0 || fd >= ft->capacity) {
return EBADF;
}
filetable_lock_handle(ft,fd);
if (ft->table[fd] == NULL) {
filetable_unlock_handle(ft,fd);
return EBADF;
}
sys_close_nocheck(fd);
filetable_unlock_handle(ft,fd);
return 0;
}
这是sys_dup2
函数的实现:
int sys_dup2(int oldfd,int newfd,int *ret_fd)
{
// when to return EMFILE or ENFILE?...
struct filetable *ft = curproc->ft;
if (oldfd < 0 || newfd < 0 ||
oldfd >= ft->capacity || newfd >= ft->capacity) {
return EBADF;
}
filetable_lock_handle(ft,oldfd);
if (ft->table[oldfd] == NULL) {
filetable_unlock_handle(ft,oldfd);
return EBADF;
}
*ret_fd = newfd;
if (oldfd == newfd) {
filetable_unlock_handle(ft,oldfd);
return 0;
}
filetable_lock_handle(ft,newfd);
if (ft->table[newfd] != NULL) {
sys_close_nocheck(newfd);
}
struct filehandle *fh = ft->table[oldfd];
filehandle_incref(fh);
ft->table[newfd] = fh;
filetable_unlock_handle(ft,oldfd);
filetable_unlock_handle(ft,newfd);
return 0;
}
我要在refcount
函数中递增sys_dup2
,并在sys_close
中递减。但是请注意,在sys_close
中,我正在检查refcount
是否为零,如果为零,则销毁文件句柄。但这是在没有获取自旋锁的情况下完成的,这意味着有可能在refcount的条件评估为true之后,可能会调度线程并且可以继续进行另一个sys_dup2
调用,复制filehandle
和之后,带有sys_close
的线程被调度,并销毁filehandle
,这使该代码不可靠。
由于两个原因,我无法在检查之前一直按住旋转锁:
2-如果条件为true并且调用函数filehandle_destroy
,则效率低下,应使用睡眠锁代替自旋锁。但是我相信我也不应该使用睡眠锁,因为在大多数情况下,该锁只能保护一行代码(例如filehandle->refcount++
)。
该设计应如何处理,以使其牢固并不会因某些不良行为而受到影响?
如果有人需要查看功能的实现,则为:
struct filehandle *filehandle_create()
{
struct filehandle *fh;
fh = kmalloc(sizeof(*fh));
if (fh == NULL) {
return NULL;
}
fh->offset_lock = lock_create("offset_lock");
if (fh->offset_lock == NULL) {
kfree(fh);
return NULL;
}
spinlock_init(&(fh->refcount_spinlock));
fh->refcount = 1;
fh->offset = 0;
fh->vnode_ptr = NULL;
fh->flags = 0;
return fh;
}
void filehandle_destroy(struct filehandle *fh)
{
//Todo check the cleaning and freeing.
KASSERT(fh != NULL);
KASSERT(fh->offset_lock != NULL);
if (fh->vnode_ptr != NULL) {
vfs_close(fh->vnode_ptr);
}
lock_destroy(fh->offset_lock);
spinlock_cleanup(&(fh->refcount_spinlock));
kfree(fh);
}
void filehandle_incref(struct filehandle *fh)
{
spinlock_acquire(&(fh->refcount_spinlock));
fh->refcount++;
spinlock_release(&(fh->refcount_spinlock));
}
void filehandle_decref(struct filehandle *fh)
{
spinlock_acquire(&(fh->refcount_spinlock));
KASSERT(fh->refcount > 0);
fh->refcount--;
spinlock_release(&(fh->refcount_spinlock));
}
struct filetable *filetable_create()
{
struct filetable *ft;
ft = kmalloc(sizeof(*ft));
if (ft == NULL) {
return NULL;
}
ft->capacity = FILETABLE_SIZE;
ft->table = kmalloc(ft->capacity * sizeof(struct filehandle *));
if (ft->table == NULL) {
kfree(ft);
return NULL;
}
ft->fh_locks = kmalloc(ft->capacity * sizeof(struct lock *));
if (ft->table == NULL) {
kfree(ft->table);
kfree(ft);
return NULL;
}
// Todo use custom names for easier debugging.
ft->table_lock = rwlock_create("table_lock");
if (ft->table_lock == NULL) {
kfree(ft->table);
kfree(ft->fh_locks);
kfree(ft);
return NULL;
}
for (int i = 0; i < ft->capacity; i++) {
// Todo use custom names for easier debugging.
ft->fh_locks[i] = lock_create("fh_lock");
if (ft->fh_locks[i] == NULL) {
for (int j = 0; j < i; j++) {
lock_destroy(ft->fh_locks[i]);
}
rwlock_destroy(ft->table_lock);
kfree(ft->table);
kfree(ft->fh_locks);
kfree(ft);
return NULL;
}
}
//Todo may use a function here (i.e. bzero)
for (int i = 0; i < ft->capacity; i++) {
ft->table[i] = NULL;
}
ft->search_start_index = 0;
return ft;
}
void filetable_destroy(struct filetable *ft)
{
KASSERT(ft != NULL);
if (ft->table != NULL) {
kfree(ft->table);
}
rwlock_destroy(ft->table_lock);
if (ft->fh_locks != NULL) {
for (int i = 0; i < ft->capacity; i++) {
lock_destroy(ft->fh_locks[i]);
}
kfree(ft->fh_locks);
}
kfree(ft);
}
void filetable_incref_all(struct filetable *ft)
{
for (int i = 0; i < ft->capacity; i++) {
if (ft->table[i] != NULL) {
ft->table[i]->refcount++;
}
}
}
void filetable_decref_all(struct filetable *ft)
{
for (int i = 0; i < ft->capacity; i++) {
if (ft->table[i] != NULL) {
KASSERT(ft->table[i]->refcount > 0);
ft->table[i]->refcount--;
}
}
}
int get_min_available_fd(struct filetable *ft,int *fd)
{
for (int i = ft->search_start_index; i < ft->capacity; i++) {
if (ft->table[i] == NULL) {
ft->search_start_index = i + 1;
*fd = i;
return 0;
}
}
return EMFILE;
}
void reclaim_fd(struct filetable *ft,int fd)
{
KASSERT(fd >= 0);
if (fd < ft->search_start_index) {
ft->search_start_index = fd;
}
}
void filetable_lock_handle(struct filetable *ft,int fd)
{
// no check for the fd since this is called from
// a trusted source.
// it's fine if the thread is descheduled after only
// acquiring the rwlock and not the sleep lock since
// the rwlock does nothing except for indicating that
// there are threads using the table currently. if
// this thread is trying to lock a fh that is already
// locked,it'll sleep,but no one will be able to
// acquire the global rw lock until all locks are
// released (including this one that is being waited
// for). that means that the order in which the
// locks are acquired matters.
rwlock_acquire_read(ft->table_lock);
lock_acquire(ft->fh_locks[fd]);
}
void filetable_unlock_handle(struct filetable *ft,int fd)
{
// does the order matter here?
lock_release(ft->fh_locks[fd]);
rwlock_release_read(ft->table_lock);
}
void filetable_lock(struct filetable *ft)
{
rwlock_acquire_write(ft->table_lock);
}
void filetable_unlock(struct filetable *ft)
{
rwlock_release_write(ft->table_lock);
}
编辑:
在@Tsyvarev的帮助下,我达到了一个我认为可能很好同步的地步。
sys_close
:
void sys_close_nocheck(int fd)
{
struct filehandle **handles = curproc->ft->table;
// will return 0 if the refcount is
// 0 after decrementing it.
if (!filehandle_decref(handles[fd])) {
filehandle_destroy(handles[fd]);
}
handles[fd] = NULL;
}
int sys_close(int fd)
{
// when to return EIO?..
struct filetable *ft = curproc->ft;
if (fd < 0 || fd >= ft->capacity) {
return EBADF;
}
filetable_lock_handle(ft,fd);
return 0;
}
sys_dup2
:
int sys_dup2(int oldfd,int *ret_fd)
{
// when to return EMFILE or ENFILE?...
struct filetable *ft = curproc->ft;
if (oldfd < 0 || newfd < 0 ||
oldfd >= ft->capacity || newfd >= ft->capacity) {
return EBADF;
}
bool are_the_same = false;
if (oldfd < newfd) {
filetable_lock_handle(ft,oldfd);
filetable_lock_handle(ft,newfd);
} else if (newfd < oldfd) {
filetable_lock_handle(ft,newfd);
filetable_lock_handle(ft,oldfd);
} else {
filetable_lock_handle(ft,oldfd);
are_the_same = true;
}
struct filehandle *fh = ft->table[oldfd];
// filehandle_incref would return 0 in
// case the refcount was 0 when trying
// to increment it. if that's the case,// then this filehandle is in the middle
// of being destroyed. Equivelent for
// having a NULL in this slot.
if (fh == NULL || !filehandle_incref(fh)) {
filetable_unlock_handle(ft,oldfd);
if (!are_the_same) {
filetable_unlock_handle(ft,newfd);
}
return EBADF;
}
*ret_fd = newfd;
if (are_the_same) {
filetable_unlock_handle(ft,oldfd);
return 0;
}
if (ft->table[newfd] != NULL) {
sys_close_nocheck(newfd);
}
ft->table[newfd] = fh;
filetable_unlock_handle(ft,newfd);
return 0;
}
filehandle_incref
和filehandle_decref
:
bool filehandle_incref(struct filehandle *fh)
{
spinlock_acquire(&(fh->refcount_spinlock));
if (fh->refcount == 0) {
spinlock_release(&(fh->refcount_spinlock));
return false;
}
fh->refcount++;
spinlock_release(&(fh->refcount_spinlock));
return true;
}
bool filehandle_decref(struct filehandle *fh)
{
spinlock_acquire(&(fh->refcount_spinlock));
KASSERT(fh->refcount > 0);
fh->refcount--;
bool
result = (bool)fh->refcount;
spinlock_release(&(fh->refcount_spinlock));
return result;
}
我不确定情况是否如此。现在同步良好了吗?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)