免费预览已结束,剩余13页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux守护进程的编程方法(含实例)/space.php?uid=23089249&do=blog&id=287770参考文献Linux信号列表(zz)Linux 守护进程的编程方法linux上编写守护进程的例程Linux下后台守护进程的编写实例一、守护进程及其特性 守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的,比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。 其特征如下:1、后台运行守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。2、独立于其运行前的环境守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。3、启动方式守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端(通常是shell)执行。 除了以上这些特征外,守护进程与普通进程基本上没有什么区别。实际上,编写守护进程也就是按照上述的守护进程特征把一个普通进程改造成为守护进程。二、 守护进程编程要点 守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同Unix环境下守护进程的编程规则并不一致。如果照搬某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。所幸的是守护进程的编程原则都是一样的,区别在于具体的实现细节。这个原则就是要满足守护进程的特征。同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来比BSD4更方便。守护进程编程要点总结如下:1、屏蔽控制终端操作信号屏蔽一些有关控制终端操作的信号。防止在守护进程没有正常运转起来时,控制终端受到干扰退出或挂起。方法如下: 1. MySignal(SIGTTOU, SIG_IGN); 2. MySignal(SIGTTIN, SIG_IGN); 3. MySignal(SIGTSTP, SIG_IGN); 4. MySignal(SIGHUP, SIG_IGN);注意,这里调用的是我自定义的为指定信号安装处理函数MySignal,而不是signal。因为是signal的语义与实现有关,所以这里改用用sigaction实现的MySignal。MySignal具体定义见后面编程实例。说明:1) SIGHUP 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也能继续下载。此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。2) SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略。用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号3) SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号。缺省时这些进程会停止执行4) SIGTTOU 类似于SIGTTIN, 当后台作业要向用户终端写数据(或修改终端模式)时收到2、处理SIGCHLD信号 处理SIGCHLD(子进程结束)信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程来处理请求。如果父进程不等待子进程结束,子进程将成为僵死进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下,可以简单地将SIGCHLD信号的操作设SIG_IGN(父进程对子进程结束状态不感兴趣,忽略子进程结束信号SIG_IGN)。 1. MySignal(SIGCHLD,SIG_IGN);这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。 另一种避免僵死进程的方法是:fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要父进程来做(参考:linux僵死进程的产生与避免)。3、后台运行为避免挂起控制终端,将Daemon放入后台执行。方法:在程序中调用fork使父进程终止,让Daemon在子进程中后台执行。 1. pid = fork();2. if (pid 0) /父进程终止;子进程继续运行3. exit(0);4、脱离控制终端,登录会话和进程组 有必要先介绍一下Linux中的进程与控制终端、登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 控制终端、登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是:第1点的基础上,调用setsid()使进程成为会话组长:1. setsid();说明:当进程是会话组长时setsid()调用失败,但第1点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。 5、禁止进程重新打开控制终端 现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端: 1. pid = fork();2. if (pid 0) /子进程终止;孙进程继续运行,孙进程不在是会话组长3. exit(0);6、重设文件创建掩模 进程从创建它的父进程那里继承了文件创建掩模,这可能会修改了守护进程所创建的文件的存取位。为防止这一点,按如下方法将文件创建掩模清除:1. umask(0);7、关闭打开的文件描述符 进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们: 1. fdTableSize = getdtablesize();2. for (fd=0; fdfdTableSize; fd+)3. close(fd);8、改变当前工作目录 进程活动时,其工作目录所在的文件系统是不能卸下的。一般需要将工作目录改变到根目录/。对于需要写运行日志的进程将工作目录改变到特定目录如/tmp。按如下方法切换工作目录1. chdir(/);9、出错记录和调试信息因为守护进程已脱离了控制终端,并且关闭了所有文件描述符,所以不能简单地把出错记录或调试信息写到标准输出或标准错误输出中。所幸的是,在Linux/Unix下有个syslogd守护进程,向用户提供了syslog()系统调用。任何程序都可以通过syslog记录信息(信息一般保存在/var/log/messages中)。 但在有些嵌入式liunx系统中,可能没有syslogd守护进程,因此,无法使用syslog()来记录信息。那就只能重定向标准输入输出了。重定向标准输入输出,需要在第4点关闭打开的文件描述符时应绕过标准输入输出,修改如下:1. RedirectStdIO(/dev/null, LOG_FILE, LOG_FILE); /重定向标准输入输出2. fdTableSize = getdtablesize();3. for (fd=3; fdfdTableSize; fd+)4. close(fd);三、单实例守护进程(Single-Instance Daemons)单实例守护进程也就是在任一时刻只运行守护进程的一个实例。这是为了避免运行多个实例时而引起的错误。例如,如果同时有多个cron守护进程实例在运行,那么每个实例都可能试图开始某个预定的操作,于是导致该操作被重复执行,这很可能会引起错误。我们可以通过记录锁机制来保证任何时候系统中只有一个该守护进程的实例在运行。具体方法是:让守护进程创建一个文件并在整个文件上加一把独占性写锁(我们把文件称为守护进程的锁文件);根据记录锁机制,只允许创建一把这样的写锁,所以在此之后如试图再创建一把这样的写锁将会失败,以此向后续的守护进程实例指明当前已有一个实例在运行。在拥有这把写锁的守护进程实例终止时,这把写锁将被自动删除,无需我们自己来清理它。可以用下面的AlreadyRunning函数来判断系统中是否已有该守护进程的实例在运行,返回值true表示有该守护进程实例在运行,否则无。1. bool AlreadyRunning()2. 3. int fdLockFile;4. char szPid32;5. struct flock fl;6.7. /* 打开锁文件 */8. fdLockFile = open(LOCK_FILE, O_RDWR | O_CREAT, LOCK_FILE_MODE);9. if (fdLockFile 0)10. 11. ErrorLog(AlreadyRunning open);12. exit(EXIT_FAILURE);13. 14.15. /* 对整个锁文件加写锁 */16. fl.l_type = F_WRLCK; /记录锁类型:独占性写锁17. fl.l_whence = SEEK_SET; /加锁区域起点:距文件开始处l_start个字节18. fl.l_start = 0; 19. fl.l_len = 0; /加锁区域终点:0表示加锁区域自起点开始直至文件最大可能偏移量为止,不管写入多少字节在文件末尾,都属于加锁范围20. if (fcntl(fdLockFile, F_SETLK, &fl) 0)21. 22. if (EACCES = errno | EAGAIN = errno) /系统中已有该守护进程的实例在运行23. 24. close(fdLockFile);25. return true;26. 27.28. ErrorLog(AlreadyRunning fcntl);29. exit(EXIT_FAILURE);30. 31.32. /* 清空锁文件,然后将当前守护进程pid写入锁文件 */33. ftruncate(fdLockFile, 0);34. sprintf(szPid, %ld, (long)getpid();35. write(fdLockFile, szPid, strlen(szPid) + 1);36.37. return false;38. 四、守护进程惯例1、守护进程的锁文件通常保存在/var/run/目录中(守护进程可能需要root用户权限才能在改目录中创建文件),并以name.pid命名,name为该守护进程或服务的名称。2、守护进程的配置文件通常保存在/etc/目录中(守护进程可能需要root用户权限才能在改目录中创建文件),并以name.conf命名,name为该守护进程或服务的名称。通常,守护进程在启动时读取其配置文件,但在此之后就不再查看它了。如果我们修改了配置文件,就必须重启守护进程,才能使配置文件生效。为了避免此麻烦,有些守护进程捕获SIGHUP信号,当接收到该信号时,重新读取配置文件(因为守护进程已与控制终端脱离关系,控制终端SIGHUP信号对其已无用,所以重复利用它来作为重新读取配置文件信号)。3、守护进程可用命令行启动,但它们通常由某一系统初始化脚本(/etc/rc*或/etc/init.d/*)启动。如果守护进程终止时,应该自动重新启动它,我们可以在/etc/inittab中为该守护进程包括_respawn;这样,守护进程终止时init会自动重新启动该守护进程。五、守护进程编程实例 守护进程实例先通过InitDaemon函数将普通进程改造成守护进程。然后,判断是否已有该守护进程的实例在运行,若有则退出运行;若无则先阻塞SIGHUP和SIGUSR1信号,然后为SIGHUP和SIGUSR1信号分别安装了信号处理函数,SIGHUP用于通知守护进程重新读取配置文件;SIGUSR1用于通知守护进程执行服务,也就是用echo打印从配置文件读到的信息。最后,进入一个死循环(守护进程主体):通过sigsuspend挂起守护进程等待信号发生,然后重新读取配置文件或执行服务,再挂起守护进程等待信号发生。该实例通过sigprocmask、sigsuspend和sigaction的配合使用,避免了信号SIGHUP、SIGUSR1的丢失。该实例可以选择是否通过syslog输出信息,若用syslog输出信息,编译时加上-DUSE_SYSLOG。该实例也可以选择是否忽略子进程结束信号SIGCHLD,若忽略,编译时加上-DIGN_SIGCHLD。我们可以通过命令“kill -SIGHUP 守护进程pid”,通知守护进程重新读取配置文件;通过“kill -SIGUSR1 守护进程pid”,通知守护进程执行服务。程序清单如下: 1. /*2. * test.c3. *4. * Created on: 2011-04-235. * Author: lingdxuyan6. */7.8.9. #include /* 标准输入输出定义 */10. #include /* 标准函数库定义 */11. #include /* Unix 标准函数定义 */12. #include 13. #include 14. #include 15. #include /* 文件控制定义 */16. #include /* 错误号定义 */17. #include /* 信号定义 */18. #include /* 定时器定义 */19. #include /* 可变参数定义 */20. #include /* syslog定义 */21. #include 22. #include 23.24. #define true 125. #define false 026.27. typedef unsigned char BYTE;28. typedef BYTE bool;29. typedef BYTE byte;30.31. #define MAX_BUF_SIZE 102432.33. #define CONFIG_FILE /etc/daemon.conf34. #define LOG_FILE /tmp/daemon.log35. #define LOCK_FILE /var/run/daemon.pid36. #define LOCK_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)37.38.39. static volatile sig_atomic_t g_nUpdateParameter = 1;40. static volatile sig_atomic_t g_nServer = 0;41. /static volatile int g_nUpdateParameter = 1;42. /static volatile int g_nServer = 0;43.44.45. /*46. * 功能: 写日志47. */48. void vWriteLog(int nPriority, const char *fmt, va_list va)49. 50. #ifdef USE_SYSLOG51. vsyslog(LOG_DAEMON | nPriority, fmt, va);52. #else53. FILE *stream;54. if (nPriority & LOG_ERR)55. stream = stderr;56. else57. stream = stdout;58. vfprintf(stream, fmt, va);59. fflush(stream);60. #endif61. 62. void WriteLog(int nPriority, const char *fmt, .)63. 64. va_list va;65.66. va_start(va, fmt);67. vWriteLog(nPriority, fmt, va);68. va_end(va);69. 70.71. /*72. * 功能: 写错误日志,用法类似perror73. */74. void ErrorLog(const char *str)75. 76. WriteLog(LOG_ERR, %s: %sn, str, strerror(errno);77. 78. /*79. * 功能: 写错误日志,用法类似于printf80. */81. void ErrorLogFmt(const char *fmt, .)82. 83. va_list va;84.85. va_start(va, fmt);86. vWriteLog(LOG_ERR, fmt, va);87. va_end(va);88. 89.90. /*91. * 功能: 写日志,用法类似printf92. */93. void InfoLog(const char *fmt, .)94. 95. va_list va;96.97. va_start(va, fmt);98. vWriteLog(LOG_INFO, fmt, va);99. va_end(va);100. 101.102. /*103. * 功能: 重定向标准输入输出104. */105. void RedirectStdIO(char *szInFile, char *szOutFile, char *szErrFile)106. 107. int fd;108.109. if (NULL != szInFile)110. 111. fd = open(szInFile, O_RDONLY | O_CREAT, 0666);112. if (fd 0)113. 114. / 标准输入重定向115. if (dup2(fd, STDIN_FILENO) 0)131. 132. / 标准输出重定向 133. if (dup2(fd, STDOUT_FILENO) 0)149. 150. / 标准错误重定向 151. if (dup2(fd, STDERR_FILENO) 0)152. 153. ErrorLog(RedirectIO dup2 error);154. exit(EXIT_FAILURE);155. 156.157. close(fd);158. 159. else160. ErrorLogFmt(RedirectStdIO open %s: %sn, szErrFile, strerror(errno);161. 162. 163.164. /*165. * 功能: 读取配置文件SIGHUP信号处理函数166. */167. void UpdateHandler(int nSigNum)168. 169. g_nUpdateParameter = 1;170. 171.172. /*173. * 功能: 折行服务SIG_USR1信号处理函数174. */175. void ServerHandler(int nSigNum)176. 177. g_nServer = 1;178. 179.180. /*181. * 功能:确保任一时刻系统中只有一个该守护进程实例在运行182. */183. bool AlreadyRunning()184. 185. int fdLockFile;186. char szPid32;187. struct flock fl;188.189. /* 打开锁文件 */190. fdLockFile = open(LOCK_FILE, O_RDWR | O_CREAT, LOCK_FILE_MODE);191. if (fdLockFile 0)192. 193. ErrorLog(AlreadyRunning open);194. exit(EXIT_FAILURE);195. 196.197. /* 对整个锁文件加写锁 */198. fl.l_type = F_WRLCK; /记录锁类型:独占性写锁199. fl.l_whence = SEEK_SET; /加锁区域起点:距文件开始处l_start个字节200. fl.l_start = 0; 201. fl.l_len = 0; /加锁区域终点:0表示加锁区域自起点开始直至文件最大可能偏移量为止,不管写入多少字节在文件末尾,都属于加锁范围202. if (fcntl(fdLockFile, F_SETLK, &fl) 0)203. 204. if (EACCES = errno | EAGAIN = errno) /系统中已有该守护进程的实例在运行205. 206. close(fdLockFile);207. return true;208. 209.210. ErrorLog(AlreadyRunning fcntl);211. exit(EXIT_FAILURE);212. 213.214. /* 清空锁文件,然后将当前守护进程pid写入锁文件 */215. ftruncate(fdLockFile, 0);216. sprintf(szPid, %ld, (long)getpid();217. write(fdLockFile, szPid, strlen(szPid) + 1);218.219. return false;220. 221.222. /*223. * 功能:设置信号nSigNum的处理函数为handler,在调用该信号处理函数前.若handler不为SIG_DEF或SIG_IGN,则系统会将该信号添加到信号屏蔽字中;信号处理函数返回后,信号屏蔽字恢复到原先值.这样,可保证在处理指定信号时,如果该信号再次发生,那么它会被阻塞到上一信号处理结束为止.不过,要是此时信号发生了多次,在对该信号解除阻塞后,也只会调用一次信号处理函数224. */225. typedef void (*sighandler)(int);226. sighandler MySignal(int nSigNum, sighandler handler)227. /void ( *Signal(int nSigNum, void (*handler)(int) )(int) 228. 229. struct sigaction saNew, saOld;230.231. saNew.sa_handler = handler;232. sigemptyset(&saNew.sa_mask);233. if (SIG_DFL != handler & SIG_IGN != handler)234. sigaddset(&saNew.sa_mask, nSigNum);235.236. saNew.sa_flags = 0;237. if (SIGALRM = nSigNum)238. 239. /不重启该信号中断的系统调用240. #ifdef SA_INTERRUPT241. saNew.sa_flags |= SA_INTERRUPT;242. #endif243. 244. else245. 246. /重启该信号中断的系统调用247. #ifdef SA_RESTART248. saNew.sa_flags |= SA_RESTART;249. #endif250. 251.252. if (sigaction(nSigNum, &saNew, &saOld) 0) /父进程终止运行;子进程过继给init进程,其退出状态也由init进程处理,避免了产生僵死进程284. exit(EXIT_SUCCESS);285. else if (pid 0) /子进程终止运行;孙进程过继给init进程,其退出状态也由init进程处理,避免了产生僵死进程301. exit(EXIT_SUCCESS);302. else if (pid 0)303. 304. ErrorLog(InitDaemon fork(child);305. exit(EXIT_FAILURE);306. 307.308. /* 6、重设文件创建掩模 309. */310. umask(0);311.312. /* 7、关闭打开的文件描述符 313. */314. RedirectStdIO(/dev/null, LOG_FILE, LOG_FILE); /重定向标准输入输出315. fdTableSize = getdtablesize();316. for (fd=3; fd= 0)338. szParameternRet - 1 = 0;339. fclose(stream);340. InfoLog(ReadConfigFile sucesss!n);341. 342. else343. ErrorLogFmt(ReadConfigFile fopen %s: %sn, CONFIG_FILE, strerror(errno);344. 345.346. /*347. * 功能: 执行守护进程的服务,也就是将szParameter用echo打印出来348. */349. void Server(char *szParameter)350. 351. int nStatus;352. pid_t pid;353.354. InfoLog(- Server -n); 355.356. pid = vfork(); /生成子进程357. #ifdef IGN_SIGCHLD358. InfoLog(ignore child SIGCHLD signal!n);359. if (0 = pid) /子进程360. 361. if (execlp(echo, echo, szParameter, NULL) 0)362. 363. ErrorLog(Server execlp);364. exit(EXIT_FAILURE);365. 366. 367. else if (pid 0) /父进程373. 374. waitpid(pid, &nStatus, 0); /等待子进程结束,否则子进程会成为僵死进程,一直存在,即便子进程已结束执行375. 376. else if (0 = pid) /子进程377. 378. pid = vfork(); /生成孙进程379. if (pid 0) 380. 381. exit(EXIT_SUCCESS); /子进程退出,孙进程过继给init进程,其退出状态也由init进程处理,与原有父进程无关382. 383. else if (0 = pid) /孙进程384. 385. if (execlp(echo, echo, szParameter, NULL) 0)386. 387. ErrorLog(Server execlp);388. exit(EXIT_FAILURE);389. 390. 391. else392. 393. ErrorLog(Server vfork(child); 394. 395. 396. else397. 398. ErrorLog(Server vfork(parent); 399. 400. #endif401. 402.403. int main()404. 405. time_t t;406. sigset_t sigNewMask, sigOldMask;407. char szParameterMAX_BUF_SIZE;408.409. /将普通进程改造成守护进程410. InitDaemon();411. if (AlreadyRunning() /若系统中已有该守护进程的实例在运行,则退出412. 413. ErrorLogFmt(Daemon already running!n);414. exit(EXIT_FAILURE);415. 416.417. /阻塞SIGHUP信号和SIGUSR1信号418. sigemptyset(&sigNewMask);419. sigaddset(&sigNewMask, SIGHUP);420. sigaddset(&sigNewMask, SIGUSR1);421. if (sigprocmask(SIG_BLOCK, &sigNewMask, &sigOldMask) /etc/daemon.conf rootubuntu:/home/lingd/arm/test# cat /etc/daemon.conf 123rootubuntu:/home/lingd/arm/test# pgrep test3472rootubuntu:/home/lingd/arm/test# kill -SIGHUP 3472#让守护进程重新读取配置文件rootubuntu:/home/lingd/arm/test# cat /tmp/daemon.log Daemon 3472 start at Wed May 11 11:45:56 2011- ReadConfigFile -ReadConfigFile fopen /etc/daemon.conf: No such file or directory- ReadConfigFile -ReadConfigFile sucesss!rootubuntu:/home/lingd/arm/test# kill -SIGUSR1 3472#让守护进程运行服务,也就是用echo打印先前从配置文件中读取到的信息rootubuntu:/home/lingd/arm/test# cat /tmp/daemon.log Daemon 3472 start at Wed May 11 11:45:56 2011- ReadConfigFile -ReadConfigFile fopen /etc/daemon.conf: No such file or directory- ReadConfigFile -ReadConfigFile sucesss!- Server -123rootubuntu:/home/lingd/arm/test# ./test#再次运行守护进程rootubuntu:/home/lingd/arm/test# cat /tmp/daemon.log Daemon 3472 start at Wed May 11 11:45:56 2011- ReadConfigFile -ReadConfigFile fopen /etc/daemon.conf: No such file or directory- ReadConfigFile -ReadConfigFile sucesss!- Server -123Daemon already running!#提示已有守护进程实例在运行2、使用syslog记录信息,同时忽略子进程结束信息SIGCHLDrootubuntu:/home/lingd/arm/test# gcc -Wall -DUSE_SY
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 包装设计师多选题习题库及参考答案
- 2025~2025专升本考试题库及答案第522期
- 局学雷锋主题活动工作总结-活动总结范文-
- 2025年“安康杯”活动竞赛培训复习题库及答案
- 全国大学生双碳科普知识竞赛答案
- 《创新思维与创业》复习题
- 中级经济师考试金融专业考前预测模拟模拟试题及答案2
- 华二自招练习题(4)-内部资料
- 中级经济师考试题库1000题
- 《个人与团队管理》期末复习选择题题库
- 工贸作业票管理办法
- 幼儿园儿童行为观察计划
- 高三心理健康护航指南
- 徳龙全自动咖啡机ECAM 22.110.SB 中文使用说明书
- 轨道交通场地平整施工方案及安全措施
- 初一生物教师培训课件
- 食品车间6S管理
- 自体输血知识培训课件
- 2025至2030中国酒店洗涤用品行业市场发展现状及竞争格局与投资发展报告
- 生态系统的信息传递课件高二上学期生物人教版选择性必修2
- 美容医院药剂管理制度
评论
0/150
提交评论