进程组和会话和线程
创建会话
6点注意事项
- 调用进程不能是进程组的组长, 该进程会变成新会话的首进程
- 该进程成为一个新进程的组长进程
- 需要有root权限
- 新会话丢弃原有的控制终端, 该会话无控制终端, 即无法与用户交互
- 若调用进程是组长进程, 则会出错返回
- 建立新会话时, 先调用fork, 父进程终止, 子进程调用
setsid
getsid函数
作用: 查看当前进程在的会话的id
pid_t getsid(pid_t pid);
参数:
pid: 要查看的进程的pid
0 : 本组的会话id
返回值:
成功: 当前的会话id
失败: -1, 设置errno
setsid函数
作用: 当当前进程不是组的组长时, 则创建一个新的会话, 然后使其成为会话组长和组组长
pid_t setsid(void);
返回值:
成功: 返回调用的进程的会话ID
失败: -1, 设置errno
守护进程
是linux中后台服务进程, 通常独立于控制终端, 并且周期性的执行某种任务或等待处理某些发生的事情, 一般采用以d结尾的名字
守护进程的特点:
- 没有控制终端
- 不能和用户直接交互
- 不受用户的登陆,注销的影响
- 始终运行
创建守护进程模型
-
创建子进程, 父进程退出
-
在子进程中创建新会话
setsid()
-
改变当前目录位置
chdir()
-
重设文件权限掩码
umask()
防止继承的文件创建屏蔽字拒绝某些权限
-
关闭/重定向文件描述符
因为继承打开的文件不会用到, 浪费系统资源, 无法卸载
重定向: 将之前的文件重定向到
/dev/null
-
开始执行守护进程核心工作, 守护进程退出处理程序模型
while()
chdir函数
作用: 改变当前工作目录的位置, 防止占用可卸载文件系统
int chdir(const char *path);
参数:
path: 目标目录的路径
返回值:
成功: 0
失败: -1, 设置errno
umask函数
作用:
mode_t umask(mode_t mask);
参数:
mask: 8进制的值
返回值:
成功: 之前设置的值
线程的概念
线程是轻量级进程
进程和线程的区别:
- 进程有独立的地址空间,有独立的
pcb
- 线程无独立的地址空间,有独立的
pcb
在linux中, 线程是最小的执行单元, 进程是最小的资源分配单元
查看pid
程序的线程
ps -Lf <pid>
liux内核实现原理
- 创建线程使用的底层函数与进程一样
clone
- 从内核看, 进程和线程一样, 都有自己的不同的pcb, 但是pcb中指向内存资源的三级页表是相同的
- 进程可以蜕变成线程
- 线程是最小的执行单元, 进程是最小的资源分配单元
线程共享的资源
-
文件描述符
-
每种信号的处理方式
信号处理方式: 哪个线程抢到了,哪个线程就处理他
-
当前工作目录
-
用户id和组id
-
内存地址空间
(.text/ .data/ .bss/ 共享库)
线程非共享资源
-
线程id
-
处理器现场和栈指针(内核栈)
-
独立的栈空间(用户栈空间)
-
errno变量(全局变量)
-
信号屏蔽字
可以指定某一个线程来处理一个特定的信号
-
调度优先级
线程的优点/缺点
优点:
- 提高程序的并发性
- 开销小
- 数据通信,共享数据方便
缺点:
- 库函数, 不稳定
- 调试, 编写困难
- 对信号支持不好
线程控制原语
检查出错返回值
不能使用perror()
!!
fprintf(stderr, "xxx error: %s\n", strerror(ret));
pthread_self函数
作用:获取线程id
pthread_t pthread_self(void);
返回值:
当前线程的id
pthread_create函数
作用:创建一个新进程
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);
参数:
thread: 传出参数, 新创建线程的线程id
attr: 线程属性, 默认为NULL
start_routine: 子线程回调函数, 创建成功, pthread_create函数返回时,该函数会被自动调用
arg: start_routine的参数, 若没有,则为NULL
返回值:
成功: 0
失败: 错误号, 且thread为定义
pthread_exit函数
作用: 退出当前的线程(包括主线程)
noreturn void pthread_exit(void *retval);
参数:
retval: 推出值, 无推出值时, NULL
返回值:
无
比较:
exit()
退出当前进程return
返回到调用者pthread_exit()
推出当前线程
pthread_join函数
作用:阻塞地回收指定的线程, 并且可以获取等待线程的返回值
int pthread_join(pthread_t thread, void **retval);
参数:
thread:传入参数, 待回收的线程id
reval: 传出参数, 为待回收的线程的退出值
若线程异常结束, 值为-1
返回值:
成功: 0
失败: 错误号
pthread_cancel函数
作用:杀死线程, 需要一个取消点(保存点)
int pthread_cancel(pthread_t thread);
参数:
thread: 待杀死的线程id
返回值:
成功: 0
失败: 错误号
注意:
- 如果子线程没有到达取消点, 则
pthread_cancel
无效,此时可以手动添加取消点pthread_testcancel()
- 成功被
pthread_cancel()
杀死的线程, 返回-1, 使用pthread_join
来回收该值
pthread_detach函数
作用: 将指定的线程分离
int pthread_detach(pthread_t thread);
参数:
thread: 待分离的线程id
返回值:
成功: 0
失败: 错误号
分离了以后,当线程终止的时候,线程会自己回收自己的pcb
, 无需主线程用join
来回收
线程控制原语 | 进程控制原语 |
---|---|
pthread_create | fork |
pthread_self | getpid |
pthread_exit | exit |
pthread_join | wait/waitpid |
pthread_cancel | kill |
pthread_detach |
线程属性
typedef struct
{
int __detachstate; // 线程的分离状态
int __schedpolicy; // 线程的调度策略
struct sched_param __schedparam; // 线程的调度参数
int __inheritsched; // 线程的继承性
int __scope; // 线程的作用域
size_t __guardsize; // 线程的缓冲区大小
int __stackaddr_set; // 线程的栈设置
void* __stackaddr; // 线程的栈位置
size_t __stacksize; // 线程的栈大小
} pthread_attr_t;
线程属性初始化
注意: 应该先初始化线程属性, 再pthread_create
创建线程
作用:初始化线程属性
int pthread_attr_init(pthread_attr_t *attr);
参数:
attr: 传出参数, 初始化以后的属性
返回值:
成功: 0
失败: 错误号
作用: 销毁线程属性所占用的资源
int pthread_attr_destroy(pthread_attr_t *attr);
参数:
attr: 待销毁的线程属性
返回值:
成功: 0
失败: 错误号
设置线程分离
作用:设置当前线程分离的属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数:
attr: 传出参数, 待修改的属性值
detachstate: 线程分离的属性值
PTHREAD_CREATE_DETACHED(分离的)
PTHREAD_CREATE_JOINABLE(未分离的,默认的)
返回值:
成功: 0
失败: 错误号
作用: 获取当前线程分离的属性
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
参数:
attr: 已初始化的线程属性
detachstate: 传出参数, 当前线程的状态
返回值:
成功: 0
失败: 错误号
实例
通过线程属性将一个线程设置为分离态
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, tfn, NULL);
pthread_attr_destroy(&attr);
线程使用注意事项
- 主线程退出, 其他线程推出, 则用
pthread_exit
- 避免僵尸线程
pthread_join
pthread_detach
pthread_create
, 指定分离属性
malloc
和mmap
申请的内存可被其他的线程释放- 避免在多线程中调用
fork
, 除非马上exec
.子进程中只有fork
线程存在, 其他的进程均要pthread_exit
(即,只存活了调用fork
的进程) - 信号和多线程不要同时用