1.进程标识
- 进程ID是唯一的,但也是可复用的,大部分系统采用延迟复用算法
- ID为0的通常是调度进程,常被称为交换进程,是内核的一部分,不执行磁盘上的程序(所以也叫系统进程)
- 进程ID为1的通常是init进程,自举过程由内核调用,超级用户特权运行,但是普通的用户进程,不会终止
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
2、fork()函数
#include <unistd.h>
pid_t fork(void);
- 子进程返回0
- 父进程返回子进程的进程ID
- fork之后经常跟exec,所以采用了写时复制技术
- 文件共享:
- 打开的文件描述符的复制类似于执行了dup()
- 父子进程共享同一文件偏移量
- 父子进程的区别:
- 子进程不继承文件锁
- 子进程未处理的闹钟被清除
- 子进程的未处理信号集设置为空集
3、vfork()
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
可移植的程序不应该使用这个函数
- vfork()创建的子进程并不会完全复制父进程的地址空间,因为会马山调用exec
- 子进程在调用exec()前在父进程空间中运行
- vfork保证子进程先运行,子进程调用exec或exit后父进程才恢复运行
4、exit()
-
进程有5中正常终止的方法:
- main()中return
- exit()
- _[eE]xit
- 最后一个线程执行return
- 最后一个线程调用pthread_exit()
-
进程的3中异常终止状态
- 调用abort()
- 接收到某些信号
- 最后一个线程对"取消"作出响应
-
父进程在子进程前终止,子进程编程僵尸进程
-
子进程在父进程前终止,内核为子进程保存一定量的信息(包括进程ID,终止状态,CPU使用时间等)
5、wait()和waitpid()
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
- 进程终止时向父进程发送SIGCHLD信号
- 调用wait时:
- 子进程还活着则阻塞
- 以终止则立即返回
- 没有任何子进程则出错返回
- 返回值指示退出状态、信号编号、是否产生core文件等
WIFEXITED(status) WEXITSTATUS(status) WIFSIGNALED(status) WTERMSIG(status) WCOREDUMP(status) WIFSTOPPED(status) WSTOPSIG(status) WIFCONTINUED(status)
6、waitid()
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
- idtype
- P_PID:等待特定进程
- P_PGID:等待特定进程组中的任一子进程
- P_ALL:等待任一子进程
- options
- WCONTINUED:等待一个进程,它以前曾被停止,此后又继续,但状态尚未报告
- WEXITED:等待已退出的进程
- WNOHANG:非阻塞
- WNOWAIT:不破坏子进程的退出状态
- WSTOPPED:进程已经停止,但状态尚未报告
7、wait3()和wait4()
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3(int *status, int options,
struct rusage *rusage);
pid_t wait4(pid_t pid, int *status, int options,
struct rusage *rusage);
- 允许内核返回由终止进程及其所有子进程使用的资源概况
8、竞争条件
多个进程企图对共享数据进行处理,结果取决于进程运行顺序
- 为了避免竞争条件和轮询,进程间需要某种形式信号的发送和接收方法
9、exec()函数
#include <unistd.h>
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[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
- 进程调用exec函数时,进程执行的程序完全替换为新进程
- 进程ID不变,因为只是用磁盘上的新程序替换了当前进程的正文段、数据段、堆栈
- …(暂时用不到就不看了)
10、更改用户ID和更改组ID
int setuid(uid_t uid);
int setgid(gid_t gid);
- 在linux中,特权以及访问控制是基于用户ID和组ID的
- 更改用户ID规则
- 若进程有root权限,实际/有效/保存的设置用户ID设置为uid
- 。。。
11、解释器文件
- 起始行是:
#! pathname [optional-argument]
12、system()函数
int system(const char *command);
- command为空时,可用时返回非0值
- 返回值
- -1:fork失败或waitpid返回EINTR之外的值
- 127?:exec失败
- shell的终止状态:成功
13、进程会计
启动进程会计后,每当进程结束内核就写一个会计记录:包括命令名、CPU时间总量、用户ID、组ID、启动时间内等
int acct(const char *filename);
- 该函数用以启用和禁用进程会计,但至今没有一个标准做了声明,只有accton命令用了这个函数
14、用户标识
任一进程都可以得到实际用户ID和有效用户ID及组ID
#include <unistd.h>
char *getlogin(void);
int getlogin_r(char *buf, size_t bufsize);
#include <stdio.h>
char *cuserid(char *string);
- 用以获取登录名