# 目录 - [目录](#目录) - [进程基本知识](#进程基本知识) - [进程标识符`pid`](#进程标识符pid) - [进程的产生](#进程的产生) - [进程的消亡及释放资源](#进程的消亡及释放资源) - [`exec`函数族](#exec函数族) - [用户权限及组权限](#用户权限及组权限) - [观摩课:解释器文件](#观摩课解释器文件) - [`system()`函数](#system函数) - [进程会计](#进程会计) - [进程时间](#进程时间) - [守护进程](#守护进程) - [系统日志](#系统日志) # 进程基本知识 已经进入**多进程**阶段 ## 进程标识符`pid` 类型`pid_t`,传统意义上是一个16位有符号整型数。 命令`ps` 常用命令:`ps axf`,`ps aux`,`ps axm`,`ps ax -L` 进程号是顺次向下使用 ```c // 返回当前进程号 pid_t getpid(void); // 返回父进程的进程号 pid_t getppid(void); ``` ## 进程的产生 `pid_t fork();` - 以**复制(duplicating)**当前进程的方式创建一个新进程 - 和`setjmp`一样,执行一次,返回两次 - 在`fork`处复制,不会从头运行 `fork`后父子进程的不同之处: 1. `fork`的返回值不一样 2. `pid`不同 3. `ppid`也不同 4. 未决信号和文件锁不继承 5. 资源利用量清0 `init`进程:**1号**,是所有进程的祖先进程 调度器的调度策略来决定哪个进程先执行 `fflush()`的重要性 ```c /* * vfork创建的子进程只能做exec或者exit * ! 基本废弃 */ pid_t vfork(void); ``` ## 进程的消亡及释放资源 ```c // 等待进程状态发生变化 pid_t wait(int *status); // 阻塞 pid_t waitpid(pid_t pid, int *status, int options); int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); wait3(); wait4(); ``` 分配法和交叉分配法,90%优先选择交叉分配法。 池类算法: 上游往池子里放任务,下游三个线程从池子里取任务。 ## `exec`函数族 eg. `bash`进程创建`primer`进程 ```c // exec函数族:替换当前进程的映像 extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); ``` ## 用户权限及组权限 `u+s`:如果文件是可执行的,则执行文件时,是以文件的拥有者的权限执行的。 ```bash -rwsr-xr-x 1 root root 68248 Mar 23 2023 /usr/bin/passwd ``` 所以普通用户执行`passwd`时,是以`root`的权限执行的。 `g+s`:如果文件是可执行的,则执行文件时,是以文件的所在组的权限执行的。 `uid`和`gid`都有三种类型: 1. `real uid`:进程的实际所有者 2. `effective uid`:进程的有效所有者 3. `saved uid`:进程的保存的有效所有者 ``` shell获取身份的流程 fork exec fork init -->--> getty -->--> login -->--> shell exec exec root root root user ``` ```c // 获取当前用户的real uid uid_t getuid(void); // 获取当前用户的effective uid uid_t geteuid(void); // 获取当前进程的real gid pid_t getegid(void); // 获取当前进程的effective gid pid_t getgid(void); // 设置当前进程的real uid int setuid(uid_t uid); // 设置当前进程的effective uid int seteuid(uid_t uid); // 设置当前进程的real gid int setgid(gid_t gid); // 设置当前进程的effective gid int setegid(gid_t gid); // 交换uid和gid (原子操作) int setreuid(uid_t ruid, uid_t euid); // 交换gid和egid (原子操作) int setregid(gid_t rgid, gid_t egid); ``` ## 观摩课:解释器文件 > unix讲究机制而非策略 脚本,后缀名是什么都可以,一般用`sh`, `exec` ```bash #!/bin/cat # some shell ``` `#!`是一种约定俗成的标记,告诉系统这个脚本应该用什么解释器来执行。 ## `system()`函数 ```c /* * 运行一个shell命令 * 调用/bin/sh */ int system(const char *command); ``` 相当于`fork+exec+wait`的封装 ## 进程会计 ```c //! freeBSD系统的方言 int acct(const char *filename); ``` ## 进程时间 ```c clock_t times(struct tms *buf); // clock_t 滴答数 struct tms{ clock_t tms_utime; /* user time */ clock_t tms_stime; /* system time */ clock_t tms_cutime; /* user time of children */ clock_t tms_cstime; /* system time of children */ } ``` ## 守护进程 1. 守护进程`PPID`为1 2. 守护进程没有控制终端,`TTY`为? 3. `PID, PGID, SID`相同 ```c pid_t setpgid(pid_t pid, pid_t pgid); pid_t getpgid(pid_t pid); pid_t getpgrp(void); //! 方言 pid_t getpgrp(psid_t pid); //! 方言 ``` - 会话(session):一个或多个进程组的集合,以`sid`为标识 `pid_t setsid(void);` `setsid`必须由非`leader`进程调用,从而创建一个新的会话。 - 前台进程组:正在与终端交互的进程组 - 后台进程组:正在运行,但不与终端交互的进程组 - 终端: 我们接触的都是虚拟终端 **单实例守护进程**:锁文件`/var/run/name.pid` 启动脚本文件:`/etc/rc*...` ## 系统日志 `syslogd`服务 ```c #include /** * 打开系统日志 * * @prarm: ident 标识符 * @prarm: option 选项 LOG_CONS, LOG_NDELAY, LOG_NOWAIT, LOG_PERROR ... * @prarm: facility 来源 LOG_USER, LOG_DAEMON, LOG_KERN, LOG_LOCAL0~7 ... */ void openlog(const char *ident, int option, int facility); /** * 记录系统日志 * * @prarm: priority 优先级 以 ERR 与 WARNING 为分界点 * @prarm: format 格式化字符串 * @prarm: ... 格式化参数 */ void syslog(int priority, const char *format, ...); /** * 关闭系统日志 */ void closelog(void); ``` ```bash sudo tail /var/log/messages # 老师 journalctl -r # 我的debian ```