首页 > 其他分享 >【BUAA S4 OS】Lab4challenge sigaction实现

【BUAA S4 OS】Lab4challenge sigaction实现

时间:2024-09-24 19:54:27浏览次数:3  
标签:__ set int BUAA S4 sig env tf OS

文章目录

sigaction简介

当一个信号被发送给一个进程时, 内核会中断进程的正常控制流,转而执行与该信号相关的用户态处理函数进行处理

执行该处理函数前,会将该信号所设置的信号屏蔽集加入到进程的信号屏蔽集中,在执行完该用户态处理函数后,又会将恢复原来的信号屏蔽集

任务描述

sigaction结构体用于设置所需要处理的信号集及其对应的处理函数

sigset_t使用32位表示MOS所需要处理的[1,32]信号掩码,对应位为1表示阻塞,为0表示未被阻塞

sigset_tsigaction结构体定义如下:

typedef struct sigset_t {
    uint32_t sig;
} sigset_t;

struct sigaction {
    void     (*sa_handler)(int); //信号的处理函数
    sigset_t   sa_mask;
};

数据结构、宏等设计

信号相关设置

此处挂起信号队列沿用此前MOS中设计的TAILQ结构并使用相关函数,实现参考 kern\env.cenv_sched_list 相关部分

// include/signal.h
// 信号掩码控制宏 __how的取值
#define SIG_BLOCK (0)	// 添加__set到当前掩码
#define SIG_UNBLOCK (1) // 从当前掩码中移除__set
#define SIG_SETMASK (2) // 设置当前掩码为__set

// SIGNUM编号
#define SIGINT (2)
#define SIGILL (4)
#define SIGKILL (9)
#define SIGSEGV (11)
#define SIGCHLD (17)
#define SIGSYS (31)

// 信号数量
#define SIG_MAX (32) 

// 掩码结构体
typedef struct sigset_t {
	uint32_t sig;
} sigset_t;

// 信号操作结构体
typedef void (*sa_handler)(int);
struct sigaction {
    void     (*sa_handler)(int);
    sigset_t   sa_mask;
};

// 信号描述符
struct signal {
	TAILQ_ENTRY(signal) sig_link;
	int signum;
	int time;
};

// 挂起的信号队列
TAILQ_HEAD(Sig_pend_list, signal);

Env结构体添加成员

// env.h
struct Env {
	// lab4-challenge
	u_int env_user_sighand_entry;	// 用户态信号处理函数入口
	u_int now_sig_time;		// 记录当前正在处理的信号的到达时间,重入处理的时候用
	u_int is_handling_SIGKILL;		// 判定现在正在被处理的是否是SIGKILL信号,若是则为1,反之为0
	u_int sig_exist[32];	// 每种信号在当前进程的存在性判断,保证每种信号的唯一性
	sigset_t sig_blocked;	       // 信号掩码
	struct Sig_pend_list sig_pend_list;	// 挂起信号队列
	struct sigaction sighand[32];  // 信号注册数组
};

错误返回值设置

// include/error.h
#define E_SIG 1    // 实际运用一般都是 return -E_SIG

头文件中添加相关函数声明

由于没有太多技术含量,此处省略,在实现具体函数后到相应头文件中添加即可

初始化与全局变量设置相关前置操作

// kern/env.c
// lab4-challenge
int sigsTime = 1;    // 用于标记信号到达时间,越小则到达时间越早
struct signal signals[SIG_MAX] __attribute__((aligned(PAGE_SIZE)));    // 进程信号数组

int env_alloc(struct Env **new, u_int parent_id) {
	// lab4-challenge
	// 最初都初始化为0
	e->sig_blocked.sig = 0;
	e->env_user_sighand_entry = 0;
	e->sig_pend_cnt = 0;
	e->now_sig_time = 0;
	e->is_handling_SIGKILL = 0;
	TAILQ_INIT(&e->sig_pend_list);
	for(int i=0; i<32; i++){
		e->sig_exist[i] = 0;
	}
	for(int i=0; i<32; i++){
		e->sighand[i].sa_handler = NULL;
		e->sighand[i].sa_mask.sig = 0;
	}
}

新增相关系统调用

统一流程

添加系统调用 syscall_func(u_int envid, ......)

  1. user/include/lib.h 中添加 void syscall_func(u_int envid, ......);

  2. user/lib/syscall_lib.c 中添加

    void syscall_func(u_int envid, ......) { 
    	......
    	msyscall(SYS_func, envid, ......);
    }
    
  3. include/syscall.h 中的 enumMAX_SYSNO 前添加 SYS_func,

  4. kern/syscall_all.cvoid *syscall_table[MAX_SYSNO] 中加上 [SYS_func] = sys_func, (注意与前一步的对应)

  5. kern/syscall_all.cvoid *syscall_table[MAX_SYSNO] 之间完成函数
    void sys_func(u_int envid, ......); 的具体实现

新增功能实现所需系统调用的具体实现

  • sys_ 具体实现

    // kern/syscall_all.c
    // 信号注册
    int sys_sigaction(int signum, const struct sigaction *newact, 
    														\struct sigaction *oldact) {
    	struct Env *e;
    	struct sigaction *sig = NULL;
    	// 只需考虑signum小于或等于32的情况,超出该范围返回-1
    	if (signum < 1 || signum > SIG_MAX) {
    		return -E_SIG;
    	}
    	e = curenv;
    	sig = &e->sighand[signum - 1];
    	if (oldact) {
    		*oldact = *sig;
    	}
    	if (newact) {
    		*sig = *newact;
    	}
    	return 0;
    }
    
    // 设置进程的信号处理函数入口地址
    int sys_set_sighand_entry(u_int envid, u_int func) {
    	struct Env *env;
    	try(envid2env(envid, &env, 1));
    	env->env_user_sighand_entry = func;
    	return 0;
    }
    
    // 向进程发送信号
    extern int sigsTime;
    extern struct signal signals[SIG_MAX] __attribute__((aligned(PAGE_SIZE)));
    int sys_sendsig(u_int envid, int sig) {
    	struct Env *e;
    	// 当envid对应进程不存在,或者sig不符合定义范围时,返回异常码-1
    	if ( sig < 1 || sig > SIG_MAX || envid2env(envid, &e, 0) != 0 ) {
    		return -E_SIG;
    	};
    	// 当进程中已经有同种信号,则不再接收
    	if ( e->sig_exist[sig-1] == 1 ){
    		return 0;
    	}
    	// 将信号添加进对应进程的信号处理队列
    	signals[sig-1].signum = sig;
    	signals[sig-1].time = sigsTime;
    	struct signal *s = (struct signal *)(&signals[sig-1]);
    	e->sig_exist[sig-1] = 1;
    	TAILQ_INSERT_HEAD(&e->sig_pend_list, (s), sig_link);
    	sigsTime++;
    	e->sig_pend_cnt++;
    	return 0;
    }
    
    // 根据__how的值更改当前进程的信号屏蔽字
    // __set是要应用的新掩码,__oset(如果非NULL)则保存旧的信号屏蔽字
    // __how可以是SIG_BLOCK(添加__set到当前掩码)、SIG_UNBLOCK(从当前掩码中移除__set)
    //         或SIG_SETMASK(设置当前掩码为__set)
    int sys_sigprocmask(int __how, const sigset_t * __set, sigset_t * __oset){
    	struct Env *e;
    	sigset_t *sigset;
    	e = curenv;
    	sigset = &e->sig_blocked;
    	if (__oset!=NULL) {
    		*__oset = *sigset;
    	}
    	if (__set!=NULL) {
    		switch (__how) {
    			case SIG_BLOCK:
    				sigset->sig |= __set->sig;
    				break;
    			case SIG_UNBLOCK:
    				sigset->sig &= ~__set->sig;
    				break;
    			case SIG_SETMASK:
    				sigset->sig = __set->sig;
    				break;
    			default:
    				return -E_SIG;
    		}
    	}
    	return 0;
    }
    
    // 检查信号__signo是否是__set信号集的成员。如果是,返回1;如果不是,返回0。
    int _sigismember(const sigset_t *__set, int __signo){
    	if (__set == NULL){
    		return 0;
    	}
    	__signo -= 1;
    	return 1 & (__set->sig >> (__signo));
    }
    
    // 获取当前被阻塞且未处理的信号集,并将其存储在__set中
    int sys_sigpending(sigset_t *__set){
    	struct signal *s = NULL;
    	int t;
    	uint32_t newSig = __set->sig;
    	TAILQ_FOREACH (s, &curenv->sig_pend_list, sig_link) {
    		// 检查掩码
    		if ( _sigismember(&curenv->sig_blocked, s->signum) ) {
    			t = s->signum;
    			newSig |= (1 << (t-1));
    		}
    	}
    	__set->sig = newSig;
    	return 0;
    }
    
    // 获取进程状态
    int sys_check_env_status(u_int envid){
    	struct Env * e;
    	try(envid2env(envid, &e, 0));
    	return e->env_status;
    }
    
  • 部分除 msyscall 还有其他操作的 syscall_

    // user/lib/syscall_lib.c
    int syscall_sigaction(int signum, const struct sigaction *newact, 
    																	\struct sigaction *oldact) {
    	if (oldact) {
    		memset(oldact, 0, sizeof(oldact));
    	}
    	return msyscall(SYS_sigaction, signum, newact, oldact);
    }
    
    int syscall_sigprocmask(int __how, const sigset_t * __set, sigset_t * __oset) {
    	if (__oset) {
    		memset(__oset, 0, sizeof(__oset));
    	}
    	return msyscall(SYS_sigprocmask, __how, __set, __oset);
    }
    

信号处理流程实现

信号处理的触发

ret_from_exception 中加入跳转到 do_signal 的代码,使得每次从用户态到内核态都调用一次信号检查函数

// kern/genex.S
FEXPORT(ret_from_exception)
	// 加入跳转到do_signal的代码(每次从用户态到内核态都调用一次)
		 move    a0, sp
     addiu   sp, sp, -8
     jal     do_signal
     addiu   sp, sp, 8

各类信号的发送触发

  • SIGINTSIGKILL 一般在程序中手动触发,不会直接在MOS代码中发送

  • SIGILL

    发送契机在进行异常处理前,发现当前异常由非法指令引发,向自身发送 SIGILL

    // kern/traps.c
    void do_reserved(struct Trapframe *tf) {
        if(((tf->cp0_cause >> 2) & 0x1f) == 10){
            sys_sendsig(0, SIGILL);
            return;
        }
    }
    
  • SIGSEGV

    发送契机当前地址过低不允许访问,则向自身发送 SIGSEGV

    // kern/tlbex.c
    static void passive_alloc(u_int va, Pde *pgdir, u_int asid) {
    	if (va < UTEMP) {
    		// panic("address too low");
    		sys_sendsig(curenv->env_id, SIGSEGV);
    	}
    }
    
  • SIGCHLD

    发送契机子进程退出时,若父进程仍在运行,则子进程向父进程发送 SIGCHLD

    // kern/env.c
    void env_destroy(struct Env *e) {
    	// lab4-challenge
    	if( e->env_parent_id != 0){
    		sys_sendsig( e->env_parent_id, SIGCHLD);
    	}
    }
    
  • SIGSYS

    发送契机当前系统调用号不存在时,需要忽视(跳过)此条语句,再向自身发送 SIGSYS

    // kern/syscall_all.c
    void do_syscall(struct Trapframe *tf) {
    	int sysno = tf->regs[4];
    	if (sysno < 0 || sysno >= MAX_SYSNO) {
    		tf->regs[2] = -E_NO_SYS;
    		tf->cp0_epc += 4;
    		sys_sendsig(curenv->env_id, SIGSYS);
    		return;
    	}
    }
    

信号的注册与发送

// 信号注册函数
int sigaction(int signum, const struct sigaction *newact, struct sigaction *oldact) {
	// 确保设置好信号处理函数入口地址
	if (env->env_user_sighand_entry != (u_int)sighand_entry) {
		try(syscall_set_sighand_entry(0, (u_int)sighand_entry));
		try(syscall_set_tlb_mod_entry(0, (u_int)entry_wrapper));
	}
	int r = syscall_sigaction(signum, newact, oldact);
	return r;
}

// 信号发送函数
int kill(u_int envid, int sig) {
	// 如果sig为SIGCHLD,SIGILL,SIGSYS,SIGSEGV信号,返回异常码-1
	if (sig == SIGILL || sig == SIGSYS || sig == SIGCHLD || sig == SIGSEGV) {
		// debugf("Only MOS can send SIGCHLD & SIGILL & SIGSYS & SIGSEGV signal.\n");
		return -E_SIG;
	}
	// 确保设置好信号处理函数入口地址
	if (env->env_user_sighand_entry != (u_int)sighand_entry) {
		try(syscall_set_sighand_entry(0, (u_int)sighand_entry));
	}
	int r = syscall_sendsig(envid, sig);
	return r;
}

信号检查

  • do_signal 实现对当前挂起信号队列的检查,选择合适的信号后调用 sig_setuptf 跳转到信号处理函数入口并为其传递参数
  • 保存寄存器上下文函数 sig_setuptf 参照 kern\tlbex.cdo_tlb_mod 实现,具体参数设计见后续信号处理函数入口
// kern/env.c
// lab4-challenge
void do_signal(struct Trapframe *tf) {
	// 在为进程设置sigpri之前也可能会发生一些异常产生异常重入的现象
	// 而异常重入的时候不可能产生任何新的信号,没必要处理信号,直接return
	if (((int)tf->cp0_epc)<0){
		return;
	}
	// 如果没有待处理信号,直接返回
	if (TAILQ_EMPTY(&curenv->sig_pend_list)){
		return;
	}
	
	struct signal *s = NULL, *s_min = NULL;
	int signo = 10000;    // 初始化为10000,后续用于判断当前信号优先级是否高于之前选中的信号
	int time = 0;
	
	// 从进程的队列中取出未被阻塞的signal
	// SIGKILL优先级最高
	if (curenv->sig_exist[SIGKILL-1] == 1){
,		TAILQ_FOREACH (s_min, &curenv->sig_pend_list, sig_link) {
			if (s_min->signum == SIGKILL) {
				signo = s_min->signum;
				time = s_min->time;
				curenv->is_handling_SIGKILL = 1;
				break;
			}
		}
	}else if(curenv->is_handling_SIGKILL == 0){
		TAILQ_FOREACH (s, &curenv->sig_pend_list, sig_link) {
			// 如果当前有信号正在处理且此信号比当前信号到达时间早,则break
			if ( s->time <= curenv->now_sig_time ){
				break;
			}
			// 选取优先级最高的
			if ( s->signum < signo 
						&& ( (curenv->sig_blocked.sig & (1<<(s->signum-1))) == 0 )){
				signo = s->signum;
				time = s->time;
				s_min = s;
			}
		}
	}
	// 若存在未被阻塞的signal, 则修改进程上下文, 转到用户态的信号处理函数
	if (s_min) {
		curenv->now_sig_time = time;
		curenv->sig_exist[signo-1] = 0;
		TAILQ_REMOVE(&curenv->sig_pend_list, s_min, sig_link);
		// 保存寄存器上下文,给信号处理函数传递参数
		sig_setuptf(tf, signo);
	} else { 
		return;
	}
}

// 保存寄存器上下文(仿写do_tlb_mod)
void sig_setuptf(struct Trapframe *tf, int signum) {
	struct Trapframe tmp_tf = *tf;
	if (tf->regs[29] < USTACKTOP || tf->regs[29] >= UXSTACKTOP) {
		tf->regs[29] = UXSTACKTOP;
	}
	tf->regs[29] -= sizeof(struct Trapframe);
	*(struct Trapframe *)tf->regs[29] = tmp_tf;
	// 将信号处理需要传递的相关参数保存至异常现场栈中
	tf->regs[4] = tf->regs[29];
	tf->regs[5] = signum;
	tf->regs[6] = (unsigned int)(curenv->sighand[signum-1].sa_handler);
	uint32_t newMask = 0;
	newMask = curenv->sighand[signum-1].sa_mask.sig | curenv->sig_blocked.sig 
																									\ | (1 << (signum - 1)) ;
	tf->regs[7] = (uint32_t)newMask;
	tf->regs[29] -= sizeof(tf->regs[4]);
	tf->regs[29] -= sizeof(tf->regs[5]);
	tf->regs[29] -= sizeof(tf->regs[6]);
	tf->regs[29] -= sizeof(tf->regs[7]);
	tf->cp0_epc = curenv->env_user_sighand_entry;
}

信号的实际处理

// 执行信号
void __attribute__((noreturn)) sighand_entry(struct Trapframe *tf, int signum, 
																	\	void (*sa_handler)(int), uint32_t newMask) {
	int r;
	// 若设置了处理函数
	if (sa_handler && signum != SIGKILL ) {
		// 处理前修改进程掩码
		sigset_t oldSigset={0}, newSigset={0};
		newSigset.sig = newMask;
		r= syscall_sigprocmask(2, (sigset_t *)&newSigset, (sigset_t *)&oldSigset);
		sa_handler(signum);
		// 恢复进程原掩码
		syscall_sigprocmask(2, (sigset_t *)&oldSigset, NULL);
		// 恢复上下文
		r = syscall_set_sig_trapframe(0, tf);
		user_panic("sig_entry syscall_set_sig_trapframe returned %d", r);
	} 
	switch (signum) {
        case SIGINT: case SIGILL: case SIGKILL: case SIGSEGV: 
				// 退出用exit
            exit(); 
            user_panic("sig_entry syscall_env_destroy returned");
        default:
            r = syscall_set_sig_trapframe(0, tf);
            user_panic("sig_entry syscall_set_sig_trapframe returned %d", r);
    }
}

信号处理后上下文的信号

参考同文件中的 sys_set_trapframe 实现

// kern/syscall_all.c
int sys_set_sig_trapframe(u_int envid, struct Trapframe *tf) {
	if (is_illegal_va_range((u_long)tf, sizeof *tf)) {
		return -E_INVAL;
	}
	struct Env *env;
	try(envid2env(envid, &env, 1));
	// 当前信号处理完毕,故重置 env->now_sig_time 为 0
	env->now_sig_time = 0;
	if (env == curenv) {
		*((struct Trapframe *)KSTACKTOP - 1) = *tf;
		// return `tf->regs[2]` instead of 0, because return value overrides regs[2] on
		// current trapframe.
		return tf->regs[2];
	} else {
		env->env_tf = *tf;
		return 0;
	}
}

信号集处理函数实现

注意如果传入参数不合法,返回值为 -E_SIG

// user/lib/syscall_lib.c
// 计算两个信号集__left和__right的并集,并将结果存储在__set中
int sigorset(sigset_t *__set, const sigset_t *__left, const sigset_t *__right){
	if ( __left == NULL ||  __right == NULL ){
		return -E_SIG;
	}
	__set->sig = __left->sig | __right->sig;
    return 0;
}

// 根据__how的值更改当前进程的信号屏蔽字。__set是要应用的新掩码
// __oset(如果非NULL)则保存旧的信号屏蔽字
// __how可以是SIG_BLOCK(添加__set到当前掩码)、SIG_UNBLOCK(从当前掩码中移除__set)
//   或SIG_SETMASK(设置当前掩码为__set)
int sigprocmask(int __how, const sigset_t * __set, sigset_t * __oset){
	syscall_sigprocmask(__how, __set, __oset);
	return 0;
}

// 清空参数中的__set掩码,初始化信号集以排除所有信号。这意味着__set将不包含任何信号。(清0)
int sigemptyset(sigset_t *__set){
	if ( __set == NULL){
		return 0;
	}
	__set->sig = 0;
	return 0;
}

// 将参数中的__set掩码填满,使其包含所有已定义的信号。这意味着__set将包括所有信号。(全为1)
int sigfillset(sigset_t *__set){
	if ( __set == NULL){
		return -E_SIG;
	}
	sigemptyset(__set);
	__set->sig = ~(__set->sig);
	return 0;
}

// 向__set信号集中添加一个信号__signo。如果操作成功,__set将包含该信号。(置位为1)
int sigaddset(sigset_t *__set, int __signo){
	if ( __set == NULL){
		return -E_SIG;
	}
	if ( __signo < 1 || __signo > SIG_MAX ) {
		return -E_SIG;
	};
	__set->sig |= (1<<(__signo-1));
	return 0;
}

// 从__set信号集中删除一个信号__signo。如果操作成功,__set将不再包含该信号。(置位为0)
int sigdelset(sigset_t *__set, int __signo){
	if ( __set == NULL){
		return 0;
	}
	if ( __signo < 1 || __signo > SIG_MAX ) {
		return -E_SIG;
	};
	__set->sig &= ~(1<<(__signo-1));
	return 0;
}

// 检查信号__signo是否是__set信号集的成员。如果是,返回1;如果不是,返回0
int sigismember(const sigset_t *__set, int __signo){
	if ( __set == NULL){
		return 0;
	}
	return 1 & (__set->sig >> (__signo-1));
}

// 检查信号集__set是否为空。如果为空,返回1;如果不为空,返回0
int sigisemptyset(const sigset_t *__set){
	if ( __set == NULL){
		return 1;
	}
	return __set->sig == 0;
}

// 计算两个信号集__left和__right的交集,并将结果存储在__set中。
int sigandset(sigset_t *__set, const sigset_t *__left, const sigset_t *__right){
	if ( __left == NULL ||  __right == NULL ){
		return -E_SIG;
	}
	__set->sig = __left->sig & __right->sig;
    return 0;
}

// 获取当前被阻塞且未处理的信号集,并将其存储在__set中
int sigpending(sigset_t *__set){
	if ( __set == NULL){
		return -E_SIG;
	}
	syscall_sigpending( __set);
	return 0;
}

其他修改(踩过的坑)

寄存器Trapframe保存与传递设置

env_pop_tf 函数被调用时,它会将 curenv->env_tf(即当前进程的上下文)加载到处理器寄存器中,并且将 curenv->env_tf 的地址赋值给堆栈指针 sp。进入 do_signal 函数时,堆栈指针 sp 指向 curenv->env_tf 的位置。

假设 do_signal 函数会在其执行过程中使用堆栈保存临时数据和函数调用信息,则这些数据会覆盖 curenv->env_tf 的内容,导致进程控制块中的数据被意外修改

所以需要通过curenv->env_tf 复制到当前栈上一个临时变量 tmp_tf,然后传入 env_pop_tf 函数,从而避免直接使用进程控制块中的地址

kern/env.c 文件中 env_run 函数的末尾修改为:

- env_pop_tf(&curenv->env_tf, curenv->env_asid);
+ struct Trapframe tmp_tf = curenv->env_tf;
+ env_pop_tf(&tmp_tf, curenv->env_asid);

Q:为什么不能将 curenv->env_tf 复制到 (struct Trapframe *)KSTACKTOP - 1?

因为KSTACKTOP 是内核栈的顶部,直接在此位置存储 Trapframe 结构体稍有不慎可能导致堆栈溢出,从而覆盖其他关键的内核数据,干扰内核栈的正常使用

fork时相关设置的继承

// kern/syscall_all.c
int sys_exofork(void) {
	// lab4-challenge
	// 全部和sigaction有关的都要复制
	e->sig_blocked = curenv->sig_blocked;
	e->env_user_sighand_entry = curenv->env_user_sighand_entry;
	e->sig_pend_cnt = curenv->sig_pend_cnt;
	e->now_sig_time = curenv->now_sig_time;
	e->is_handling_SIGKILL = 0;
	e->sig_pend_list = curenv->sig_pend_list;
	for(int i=0; i<32; i++){
		e->sig_exist[i] = curenv->sig_exist[i];
	}
	for(int i=0; i<32; i++){
		e->sighand[i].sa_handler = curenv->sighand[i].sa_handler;
		e->sighand[i].sa_mask.sig = curenv->sighand[i].sa_mask.sig;
	}
}

同时,为了防止父进程没有注册信号处理函数,在fork时可以“再加一层保险”

// user/lib/fork.c
int fork(void) {
	// lab4-challenge
	if (env->env_user_sighand_entry != (u_int)sighand_entry) {
		try(syscall_set_sighand_entry(0, (u_int)sighand_entry));
	}
}

页错误处理函数入口的同步设置

在实际编码中,发现可能会出现页错误情况,报错未注册页错误处理函数。因此,在注册信号时也实现页错误处理函数入口的同步设置

由于 cow_entry 是静态函数,因此需要用一个函数将其包裹以便在其他文件中调用

// user/lib/fork.c
void entry_wrapper(struct Trapframe *tf) {
    cow_entry(tf);
}

其他踩过的坑/关键设计

  • 需要区分“打断”与“早已到”并妥善设计
    • 问题:在一个信号处理完成之前,也可能会进行一些系统调用,这些系统调用在返回之前也会扫描信号队列,怎么防止系统转而去执行更早到达的信号(早已到)?但同时,我们也要允许当前处理完成之前,晚于当前信号到达的信号(打断)能够被先处理
    • 解决思路
      1. 先在 env.c 里设置一个全局变量 sigsTime
      2. 每次发送信号的时候让 s->sig_time=sigsTime ,然后 sigsTime++
      3. Env 结构体增加成员变量 handlingSig_time ,进程每开始处理一个信号就让 curenv->handlingSig_time=s->sig_time
      4. dosignal 中根据比较 s->sig_timecurenv->handlingSig_time=s->sig_time 的大小来判断信号发出的先后
    • 不严谨的地方:发送信号的时间不严格等于信号开始被处理的时间,后续可以优化
  • 处理前修改进程掩码,处理后恢复进程原掩码的实现需要小心谨慎,要想清楚到底要恢复成什么样

标签:__,set,int,BUAA,S4,sig,env,tf,OS
From: https://blog.csdn.net/lyxjtsdmfz/article/details/142497114

相关文章

  • 408OS_PV操作大题总结
    咸鱼今年压了读者写者问题,前几年没考过。死锁的四个条件是:禁止抢占(nopreemption):系统资源不能被强制从一个线程中退出。持有和等待(holdandwait):一个线程在等待时持有并发资源。持有并发资源并还等待其它资源,也就是吃着碗里的望着锅里的。互斥(mutualexclusion):资源只能同时......
  • 华为HarmonyOS灵活高效的消息推送服务(Push Kit) - 5 发送通知消息
    场景介绍通知消息通过PushKit通道直接下发,可在终端设备的通知中心、锁屏、横幅等展示,用户点击后拉起应用。您可以通过设置通知消息样式来吸引用户。开通权益PushKit根据消息内容,将通知消息分类为服务与通讯、资讯营销两大类别,开放通知消息自分类权益。两种类型的通知消息......
  • 【HarmonyOS】开启沉浸式(全屏)并获取顶部状态栏和底部导航条高度
    鸿蒙开启沉浸式(全屏)并获取顶部状态栏和底部导航条高度1.获取主窗口constwin=windowStage.getMainWindowSync()2.开启沉浸式win.setWindowLayoutFullScreen(true)3.顶部状态栏高度//获取状态栏区域 letarea=win.getWindowAvoidArea(window.AvoidAreaType.......
  • Microsoft 365 消息速递:Microsoft 365 PnP Management Shell注册变化
    51CTOBlog地址:https://blog.51cto.com/u_13969817PnPPowerShell和CLIforMicrosoft365是社区提供的开源工具,很多Office365Admin习惯性使用脚本执行一些配置和管理策略的应用,使用名为PnPManagementShell的多租户应用程序注册来授予脚本所需的权限,然而,这个多租户应用程序注册......
  • [Paper Reading] CAPE: Camera View Position Embedding for Multi-View 3D Object De
    目录名称TL;DRMethodKeyPositionEmbeddingConstructionQueryPositionEmbeddingConstructionKey/QueryPositionEmbedding两者结合关系参考下图temporalmodelingExperiment总结与发散相关链接资料查询名称link时间:23.03机构:Baidu/华科TL;DR提出CAPE(CAmeraviewPosi......
  • OSPF 默认路由的发布原则 | 类型详解
    默认路由默认路由是指目的地址和掩码都是0的路由。当设备无精确匹配的路由时,就可以通过默认路由进行报文转发。一般多用于网络边界路由器访问互联网所需要的一条路由。同时,企业内,在精确的内部路由基础上,边界路由器通告一条默认路由,使所有访问未知目的地的数据报文都送至......
  • 如何在 CentOS 中进入 root 权限
    作为一名服务器管理员,有时您需要以root用户身份执行任务以进行管理操作。以下是两种在CentOS中执行此操作的方法:方法1:使用sudosudo命令允许您以root用户的身份执行特定命令,而无需更改用户会话。要使用此方法:在终端中键入以下命令:1sudo-i系统会提......
  • Linux系统CentOS下挂载磁盘
    1.挂载磁盘步骤总结如下1.对磁盘进行分区2.对磁盘进行格式化3.将磁盘挂载到对应目录4.设置开机自动挂载磁盘2.对磁盘进行分区2.1查看系统设备信息lsblk指令显示所有块设备信息:显示系统中所有的块设备信息,包括磁盘和分区lsblk2.2查看未挂载的磁盘fdisk-l2.3对新建的磁盘进......
  • Linux系统CentOS下挂载磁盘
    1.挂载磁盘步骤总结如下1.对磁盘进行分区2.对磁盘进行格式化3.将磁盘挂载到对应目录4.设置开机自动挂载磁盘2.对磁盘进行分区2.1查看系统设备信息lsblk指令显示所有块设备信息:显示系统中所有的块设备信息,包括磁盘和分区lsblk2.2查看未挂载的磁盘fdisk-l2.3......
  • ehviewer安卓和IOS怎样安装?(已解决)
    ehviewer是一款非常流行的办公软件套件,它提供了类似Microsoftehviewer的功能,包括文字处理(ehviewerWriter)、电子表格(ehviewerSpreadsheet)和演示文稿(ehviewerPresentation)。以下是安装和配置ehviewerOffice的详细步骤及技巧,适用于Windows操作系统。对于其他操作系统(如......