• linux 进程组和会话和线程


    进程组和会话和线程

    创建会话

    6点注意事项

    • 调用进程不能是进程组的组长, 该进程会变成新会话的首进程
    • 该进程成为一个新进程的组长进程
    • 需要有root权限
    • 新会话丢弃原有的控制终端, 该会话无控制终端, 即无法与用户交互
    • 若调用进程是组长进程, 则会出错返回
    • 建立新会话时, 先调用fork, 父进程终止, 子进程调用setsid

    getsid函数

    作用: 查看当前进程在的会话的id
    pid_t getsid(pid_t pid);
    参数:
    	pid: 要查看的进程的pid
    		0 : 本组的会话id
    返回值:
    	成功: 当前的会话id
    	失败: -1, 设置errno
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    setsid函数

    作用: 当当前进程不是组的组长时, 则创建一个新的会话, 然后使其成为会话组长和组组长
    pid_t setsid(void);
    返回值:
    	成功: 返回调用的进程的会话ID
    	失败: -1, 设置errno
    
    • 1
    • 2
    • 3
    • 4
    • 5

    守护进程

    ​ 是linux中后台服务进程, 通常独立于控制终端, 并且周期性的执行某种任务或等待处理某些发生的事情, 一般采用以d结尾的名字

    守护进程的特点:

    • 没有控制终端
    • 不能和用户直接交互
    • 不受用户的登陆,注销的影响
    • 始终运行

    创建守护进程模型

    1. 创建子进程, 父进程退出

    2. 在子进程中创建新会话

      setsid()

    3. 改变当前目录位置

      chdir()

    4. 重设文件权限掩码

      umask()

      防止继承的文件创建屏蔽字拒绝某些权限

    5. 关闭/重定向文件描述符

      因为继承打开的文件不会用到, 浪费系统资源, 无法卸载

      重定向: 将之前的文件重定向到/dev/null

    6. 开始执行守护进程核心工作, 守护进程退出处理程序模型

      while()

    chdir函数

    作用: 改变当前工作目录的位置, 防止占用可卸载文件系统
    int chdir(const char *path);
    参数:
    	path: 目标目录的路径
    返回值:
    	成功: 0
    	失败: -1, 设置errno
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    umask函数

    作用: 
    mode_t umask(mode_t mask);
    参数:
    	mask: 8进制的值
    
    返回值:
    	成功: 之前设置的值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    线程的概念

    线程是轻量级进程

    进程和线程的区别:

    • 进程有独立的地址空间,有独立的pcb
    • 线程无独立的地址空间,有独立的pcb

    在linux中, 线程是最小的执行单元, 进程是最小的资源分配单元

    查看pid程序的线程

    ps -Lf <pid>
    
    • 1

    liux内核实现原理

    • 创建线程使用的底层函数与进程一样clone
    • 从内核看, 进程和线程一样, 都有自己的不同的pcb, 但是pcb中指向内存资源的三级页表是相同的
    • 进程可以蜕变成线程
    • 线程是最小的执行单元, 进程是最小的资源分配单元

    线程共享的资源

    1. 文件描述符

    2. 每种信号的处理方式

      信号处理方式: 哪个线程抢到了,哪个线程就处理他

    3. 当前工作目录

    4. 用户id和组id

    5. 内存地址空间

      (.text/ .data/ .bss/ 共享库)

    线程非共享资源

    1. 线程id

    2. 处理器现场和栈指针(内核栈)

    3. 独立的栈空间(用户栈空间)

    4. errno变量(全局变量)

    5. 信号屏蔽字

      可以指定某一个线程来处理一个特定的信号

    6. 调度优先级

    线程的优点/缺点

    优点:

    1. 提高程序的并发性
    2. 开销小
    3. 数据通信,共享数据方便

    缺点:

    1. 库函数, 不稳定
    2. 调试, 编写困难
    3. 对信号支持不好

    线程控制原语

    检查出错返回值

    不能使用perror()!!

    fprintf(stderr, "xxx error: %s\n", strerror(ret));
    
    • 1

    pthread_self函数

    作用:获取线程id
    pthread_t pthread_self(void);
    返回值:
    	当前线程的id
    
    • 1
    • 2
    • 3
    • 4

    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为定义
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    pthread_exit函数

    作用: 退出当前的线程(包括主线程)
    noreturn void pthread_exit(void *retval);
    参数:
    	retval: 推出值, 无推出值时, NULL
    返回值:
    	无
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    比较:

    • exit()退出当前进程
    • return返回到调用者
    • pthread_exit()推出当前线程

    pthread_join函数

    作用:阻塞地回收指定的线程, 并且可以获取等待线程的返回值
    int pthread_join(pthread_t thread, void **retval);
    参数:
    	thread:传入参数, 待回收的线程id
    	reval: 传出参数, 为待回收的线程的退出值
    			若线程异常结束, 值为-1
    返回值:
    	成功: 0
    	失败: 错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    pthread_cancel函数

    作用:杀死线程, 需要一个取消点(保存点)
    int pthread_cancel(pthread_t thread);
    参数:
    	thread: 待杀死的线程id
    	返回值:
    		成功: 0
    		失败: 错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:

    • 如果子线程没有到达取消点, 则pthread_cancel无效,此时可以手动添加取消点 pthread_testcancel()
    • 成功被pthread_cancel()杀死的线程, 返回-1, 使用pthread_join来回收该值

    pthread_detach函数

    作用: 将指定的线程分离
    int pthread_detach(pthread_t thread);
    参数:
    	thread: 待分离的线程id
    返回值:
    	成功: 0
    	失败: 错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    分离了以后,当线程终止的时候,线程会自己回收自己的pcb, 无需主线程用join来回收


    线程控制原语进程控制原语
    pthread_createfork
    pthread_selfgetpid
    pthread_exitexit
    pthread_joinwait/waitpid
    pthread_cancelkill
    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    线程属性初始化

    注意: 应该先初始化线程属性, 再pthread_create创建线程

    作用:初始化线程属性
    int pthread_attr_init(pthread_attr_t *attr);
    参数:
    	attr: 传出参数, 初始化以后的属性
    返回值:
    	成功: 0
    	失败: 错误号
    
    
    作用: 销毁线程属性所占用的资源
    int pthread_attr_destroy(pthread_attr_t *attr);
    参数:
    	attr: 待销毁的线程属性
    返回值:
    	成功: 0
    	失败: 错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    设置线程分离

    作用:设置当前线程分离的属性
    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
    	失败: 错误号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    实例

    通过线程属性将一个线程设置为分离态
    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    线程使用注意事项

    • 主线程退出, 其他线程推出, 则用pthread_exit
    • 避免僵尸线程
      • pthread_join
      • pthread_detach
      • pthread_create, 指定分离属性
    • mallocmmap申请的内存可被其他的线程释放
    • 避免在多线程中调用fork, 除非马上exec .子进程中只有fork线程存在, 其他的进程均要pthread_exit(即,只存活了调用fork的进程)
    • 信号和多线程不要同时用
  • 相关阅读:
    你最关心的3D建模师“钱”途问题
    JAVA毕业设计考勤管理系统计算机源码+lw文档+系统+调试部署+数据库
    阶段二-Day18-Java新特性
    【CSS】CSS实现元素逐渐消失(实现元素透明逐渐消失/模糊)
    【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷01
    three.js——通过顶点和顶点索引创建集合体
    go语言面试
    Doris学习笔记之介绍、编译安装与部署
    2023年亚太杯数学建模思路 - 案例:异常检测
    java集合 list转map一些常用的方式(Stream流,,,)
  • 原文地址:https://blog.csdn.net/ghost_him/article/details/125614629