十四、守护进程和线程的概念、创建以及分离属性

1.守护进程

1.1 守护进程的特点

后台服务进程

独立于控制终端

周期性执行某任务

不受用户登录注销影响

一般采用以d结尾的名字(服务)

1.2 进程组

  进程的组长:

              组里边的第一进程。

              进程组的ID==进程中的组长的ID。

  进程中组长的选择:

               进程中的第一个进程。

  进程组ID的设定

               进程组的ID就是组长的进程ID。

1.3 会话

  创建一个会话注意事项:

                  不能是进程组长。

                  创建会话的进程成为新进程组的组长。

                  有些lInux版本需要root权限执行此操作。

                  创建出的新会话会丢弃原有的控制终端。

                  一般步骤;fork ,父亲死,儿子执行创建会话操作(setid)。

  获取进程所属的会话ID:

                  pid_t getsid(pid_t pid);

  创建一个会话

                  pid_t setid(void);

1.4 创建守护进程模型

  fork子进程,父进程退出

  子进程创建新会话

  改变当前工作目录chdir

  重设文件掩码

  关闭文件描述符

  执行核心工作

代码示例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main()
{
        pid_t pid;
        pid = fork();
        if(pid>0)
        {



                raise(SIGKILL);
        }
        else if(pid == 0)
        {
                setsid();
                while(1);
        }

        return 0;
}

2 线程的概念

线程(Thread) 是操作系统能够进行运算调度的最小单位,是进程中的一个执行路径。一个进程可以包含多个线程,共享进程的资源(如内存、文件句柄等),但每个线程拥有独立的程序计数器、栈和寄存器状态。线程的引入提高了程序的并发性和效率。


2.1 核心概念

  1. 线程 vs 进程

    特性

    进程

    线程

    资源分配

    独立的内存空间、文件句柄等

    共享进程资源(内存、文件等)

    切换开销

    高(需切换内存空间、内核数据结构)

    低(仅切换线程上下文)

    通信方式

    复杂(管道、信号、共享内存等)

    直接共享内存(但需同步机制)

    健壮性

    一个进程崩溃不影响其他进程

    一个线程崩溃可能导致整个进程终止

  2. 线程类型

    • 用户级线程:由用户空间的线程库管理(如 POSIX 的 pthread),轻量但无法利用多核。

    • 内核级线程:由操作系统内核直接管理,可跨 CPU 核心调度,但切换开销较高。

    • 混合模型(如 Linux 的 NPTL):结合两者优势,实现多对多映射。


2.2 线程的核心特性

  1. 共享资源
    线程共享进程的全局变量、堆内存、文件描述符等,但需通过同步机制(如互斥锁)避免冲突。

  2. 并发执行
    多个线程可并行运行(在多核 CPU 上)或分时复用 CPU(单核)。

  3. 轻量级切换
    线程上下文切换仅需保存寄存器、栈指针等少量数据。

  4. 独立执行流
    每个线程有自己的程序计数器(PC)和栈空间。


2.3 线程的应用场景

  1. 高并发服务

    • Web 服务器(如 Nginx)使用线程池处理多个客户端请求。

  2. 图形界面程序

    • 主线程处理 UI,后台线程执行耗时操作(如文件下载)。

  3. 并行计算

    • 多线程加速数据处理(如矩阵运算、机器学习训练)。

  4. 实时系统

    • 多个线程分别响应不同传感器输入。   

                                                                           

3 线程的创建        

  创建线程之后,地址空间没有变化进程退出变成了线程--主线程创建出的子线程和主线程共用地址空间主线程和子线程有各自独立的PCB-子线程的PCB是从主线程拷贝来的。主线程和子线程共享:.text、 .bss、 .data、堆、动态加载区、环境变量和命令行参数。通信:全局变量,堆。不共享:一共五个线程,栈区被平均分成五块 在Linux下: 线程就是进程-轻量级进程。对于内核来货,线程就是进程。多进程和多线程的区别: 多进程: 始终共享的资源 代码、文件描述符、内存映射区--mmap;多线程:始终共享的资源:堆、全局变量,节省资源。

3.1 创建线程

1.创建线程‐‐pthread_create
int pthread_create( pthread_t *thread), //线程ID = 无符号长整型
const pthread_attr_t *attr, //线程属性,NULL
void *(*start_routine)(void *), //线程处理函数
void *arg); //线程处理函数
参数:
pthread:传出参数,线程创建成功之后,会被设置一个合适的值
attr:默认传NULL
start_routine:子线程的处理函数
arg: 回调函数的参数
返回值:
成功:0
错误:错误号 //perror不能使用该函数打印错误信息
主线程先退出,子线程会被强制结束
验证线程直接共享全局变量
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void *myfun(void*arg)
{
        printf("son thread is %ld\n",pthread_self());
        return 0;
}
int main()
{
        int ret;
        pthread_t pthid;
        ret=pthread_create(&pthid,NULL,myfun,NULL);
        if(ret!=0)
        {
                printf("error number is %d\n",ret);
                printf("%s\n",strerror(ret));
        }
        printf("parent thread id is %ld\n",pthread_self());
        for(int i=0;i<5;i++)
        {
                printf("i is %d\n",i);
        }
        sleep(2);
        return 0;

}

  运行结果:

3.2 单个线程退出

函数原型: void pthread‐exit(void *retval);
retval指针:必须指向全局,堆
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
void *myfun(void*arg)
{
	printf("son thread is %ld\n",pthread_self());
	for(int i=0;i<5;i++)
	{
		printf("i is %d\n",i);
		if(i==2)
		{
			pthread_exit(NULL);
		}
	}
	return 0;
}
int main()
{
	int ret;
	pthread_t pthid;
	ret=pthread_create(&pthid,NULL,myfun,NULL);
	if(ret!=0)
	{
		printf("error number is %d\n",ret);
		printf("%s\n",strerror(ret));
	}
	printf("parent thread id is %ld\n",pthread_self());
	int i=0;
	while(1)
	{
		printf("i is %d\n",i);
		if(i==20)
		{
			pthread_exit(NULL);
		}
                 i++;  
	}

	sleep(2);
	return 0;

}

运行结果:

3.3 获取线程退出状态

函数原型:
int pthread_join(pthread_t pthread, void **retval)
参数:
pthread:要回收的子线程的ID
retval:读取线程退出的携带信息
传出参数
void* ptr;
pthread_join(pthid,&ptr);
指向的内存和pthread_exit参数指向地址一致
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
int num=100;
void *myfun(void*arg)
{
        printf("son thread is %ld\n",pthread_self());
        for(int i=0;i<5;i++)
        {
                printf("i is %d\n",i);
                if(i==2)
                {
                        pthread_exit(&num);
                }
        }
        return 0;
}
int main()
{
        int ret;
        pthread_t pthid;
        ret=pthread_create(&pthid,NULL,myfun,NULL);
        if(ret!=0)
        {
                printf("error number is %d\n",ret);
                printf("%s\n",strerror(ret));
        }
        printf("parent thread id is %ld\n",pthread_self());
        void *str;
        pthread_join(pthid,&str);
        printf("num is %d",*(int *)str);
        int i=0;
        while(i<10)
        {
                printf("i is %d\n",i);
                 i++;
        }
        pthread_exit(NULL);
        sleep(2);
        return 0;

}

3.4 线程分离

函数原型:int pthread_datach(pthread_t thread);
调用该函数之后不需要 pthread_join
子线程会自动回收自己的PCB
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
int num=100;
void *myfun(void*arg)
{
	printf("son thread is %ld\n",pthread_self());
	for(int i=0;i<5;i++)
	{
		printf("i is %d\n",i);
		if(i==2)
		{
			pthread_exit(&num);
		}
	}
	return 0;
}
int main()
{
	int ret;
	pthread_t pthid;
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	ret=pthread_create(&pthid,&attr,myfun,NULL);
	if(ret!=0)
	{
		printf("error number is %d\n",ret);
		printf("%s\n",strerror(ret));
	}
	printf("parent thread id is %ld\n",pthread_self());
	void *str;
	pthread_join(pthid,&str);
	printf("num is %d",*(int *)str);
	int i=0;
	while(i<10)
	{
		printf("i is %d\n",i);
                 i++;  
	}
	pthread_attr_destroy(&attr);
	pthread_exit(NULL);
	sleep(2);
	return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值