Linux系统编程(六)进程与线程
1、进程组
进程组代表一个或多个进程的集合,父进程创建子进程时默认子进程与父进程属于同一个进程组,进程组ID等于组长进程ID,(组长进程ID等于其组进程ID)
可以通过kill -SIGKILL -进程组ID 将所有的进程全部杀死。组长进程可以创建一个进程组,只要进程组中有一个进程存在,进程组就存在,与组长进程是否中止无关。
进程组生存周期:进程组创建到最后一个进程离开(中止或者转移到另一个进程)
进程可以为自己或者子进程设置进程组id。
下面举个例子:
$ cat | cat | cat | wc -l $ ps ajx PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 2943 20628 20628 2943 pts/0 20628 S+ 1000 0:00 cat 2943 20629 20628 2943 pts/0 20628 S+ 1000 0:00 cat 2943 20630 20628 2943 pts/0 20628 S+ 1000 0:00 cat 2943 20631 20628 2943 pts/0 20628 S+ 1000 0:00 wc -l $ kill -9 -20628 # 杀死进程组中的所有进程 # Killed $ kill -9 20628 # 杀死父进程 # 0 写管道被杀死返回0,最终所有进程执行完毕,返回值为0
2、会话
组长进程不能创建会话,创建一个会话可以使用setsid方法,创建一个会话要注意以下内容:
调用进程不能是进程组组长,该进程会变成新会话的首进程
该进程成为一个新进程组的组长进程
新会话丢弃原有的控制终端,该会话没有控制终端
建立新会话时,先调用fork,父进程中止,子进程调用setsid
控制终端类型:
TTY: 文字终端
pts/0: 虚拟终端
? : 无终端
3、守护进程
Daemon进程,Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某种任务或等待处理某些发生的事件,一般以d结尾的名字,守护进程不会因为用户注销而停止。
创建守护进程模型:调用setsid
a. fork创建子进程,父进程退出
b. 在子进程中创建会话
c. 改变当前工作目录 chdir,防止占用可卸载的文件系统
d. 重设文件权限掩码,防止继承的文件创建屏蔽字拒绝某些权限
e. 关闭文件描述符,继承的打开文件不会用到,浪费系统资源,无法卸载
f. 执行业务逻辑
给出一个例子:
#include#include #include #include #include int main(int argc, char** argv) { pid_t pid = fork(); if(pid < 0) { perror("fork error"); exit(1); } else if(pid > 0) { exit(0); } pid = setsid(); if(pid == -1) { perror("setsid fail"); exit(1); } chdir("/home/may"); umask(0022); close(STDIN_FILENO); int fd = open("/dev/null", O_RDWR); if(fd == -1) { perror("open /dev/null fail"); } dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); while(1); return 0; }
4、线程
进程:独立地址空间,有PCB,最小的资源分配单位
线程:有独立的PCB,但是没有独立的地址空间,最小的执行单位:LWP:light weight process,轻量级的进程
通过 $ ps -Lf pid,可以查看进程中的线程
线程共享资源
a. 文件描述符表
b. 每种信号的处理方式(mask不共享)
c. 当前工作目录
d. 用户ID 和 组ID
e. 内存地址空间(.text 代码段 / .data / .bss / .heap/ 共享库)
线程非共享资源
a. 线程id
b. 处理器现场和栈指针(内核栈)
c. 独立的栈空间
d. errno 变量(全局变量,在data段中但是不共享)
e. 信号屏蔽字
f. 调度优先级
线程优缺点
优点:
提高程序并发、开销小、数据通信共享数据方便
缺点:
库函数不稳定、调试困难、对信号支持不好
进程中创建线程之后,进程就会退化成为线程,进程可以看作为是独享进程空间的线程......