当前位置: 首页 > news >正文

Linux高并发服务器开发 第十九天(线程 进程)

目录

1.进程组和会话

2.守护进程

2.1守护进程daemon概念 

2.2创建守护进程

3.线程

3.1线程的概念

3.2线程内核三级映射

3.3线程共享

3.4线程优缺点

4.线程控制原语

4.1获取线程id

4.2创建线程

4.3循环创建N个子线

4.4子线程传参地址,错误示例

4.5线程退出

4.6线程回收

4.6.1回收示例1

4.6.2回收示例2

4.7线程分离

4.8杀死(取消)线程

4.8.1终止线程方法


1.进程组和会话

- 进程聚集成为进程组,多个进程组聚集成为会话。

- ps ajx //查看 进程组id  和 会话id

- 创建会话 setsid()

 pid_t setsid(void); 

- 组长进程不能作为新会话的首进程,因此 fork() 后,终止父进程,子进程调用 setsid() 创建会话,以自己的进程pid, 为会话id 和 进程组 id。

2.守护进程

2.1守护进程daemon概念 

- Linux后台的一些服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在后台运行。周期性的执行某种任务,或者等待某一事件的发生。 一般采用以d 结尾的命名方式。

2.2创建守护进程

int main(int argc, char *argv[])
{
    pid_t pid;
    int ret, fd;

    pid = fork();        // 创建子进程
    if (pid > 0)
        exit(0);        // 终止父进程

    pid = setsid();        // 子进程创建新会话.
    if (pid == -1)
        sys_err("setsid err");

    ret = chdir("/home/itcast/bj_40");  // 改变工作目录
    if (ret == -1)
        sys_err("chdir err");

    umask(0);            // 改变文件访问权限掩码,没有屏蔽任何权限

    close(STDIN_FILENO);// 关闭标准输入文件描述符

    fd = open("/dev/null", O_RDWR);   // fd ---> 0
    if (fd == -1)
        sys_err("open err");
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);

    while (1);            // 模拟守护进程业务

    return 0;
}

3.线程

3.1线程的概念

- Linux 系统中,线程 LWP 称之为:轻量级的进程。
- 进程:有独立的进程地址空间, 有独立的 pcb。 —— 最小资源分配单位。
- 线程:有独立的pcb,没有独立的进程地址空间。(与其他线程共享) —— 最小执行单位。

- 一个创建了线程的进程,本身也沦落 为线程。

- LWP 号: cpu 划分时间片依据。  —— 线程 最小执行单位。
- 查看LWP号命令: ps -Lf 进程pid 

3.2线程内核三级映射

- 三级映射。—— 解释了,为什么线程没有独立的进程地址空间。

3.3线程共享

- 独享:栈空间(用户栈、内核栈)errnum 
- 共享:./text  ./ordata  ./data  ./bss  heap堆 ----> 共享全局变量

3.4线程优缺点

- 优点:
    - 并发性强。  
    - 开销小。
    - 数据通信方便。
- 缺点:
    - 库函数,稳定性差。
    - 调试、编写困难
    - 对信号支持差。
- 结论:既能使用进程开发,也能使用线程开发的程序,首选 线程。

4.线程控制原语

4.1获取线程id

#include <pthread.h>

pthread_t pthread_self(void);   // 获取线程id, 在进程内部标识线程身份。

返回值:线程id

4.2创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

参1: 传出参数,新子线的 线程id
参2: 线程属性。默认传 NULL, 表使用默认属性。
参3: 子线程回调函数。pthread_create 调用成功,该函数会被自动调用起来。
参4: 参3 的参数。

返回值:
    成功:0
    失败:直接返回错误号!

- 线程中处理出错, 只能使用 strerror() , 不能使用 perror() ,举例如下:

#include <string.h>

char *strerror(int errnum);
fprintf(stderr, "xxx error:%s\n", strerror(错误号));

- 创建子线程

// 子线程主函数
void *tfn(void *arg)
{
    printf("tfn : pid = %d, pthread_id = %lu\n", getpid(), pthread_self());
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid;

    // 创建子线程
    int ret = pthread_create(&tid, NULL, tfn, NULL);
    if (ret != 0)
        fprintf(stderr, "pthread_create err:%s\n", strerror(ret));

    printf("main : pid = %d, pthread_id = %lu\n", getpid(), pthread_self());
    sleep(1);        // 给子线程执行时间

    return 0;        // 释放进程地址空间
}

4.3循环创建N个子线

- 每个子线程打印,自己是第几个被创建出来。

void *tfn(void *arg)
{
    int i = (int)(long)arg;
    printf("I'm %dth thread: pid= %d, tid= %lu\n", i+1, getpid(), pthread_self());
    return NULL;
}

int main(int argc, char *argv[])
{
    int i, ret;
    pthread_t tid;

    for (i = 0; i < 5; i++) 
    {
        ret = pthread_create(&tid, NULL, tfn, (void *)(long)i);
        if (ret != 0)
            fprintf(stderr, "pthread_create err:%s\n", strerror(ret));
    }
    //sleep(i);
    usleep(10000);
    printf("I'm main thread: pid= %d, tid= %lu\n", getpid(), pthread_self());
    return 0;
}

4.4子线程传参地址,错误示例

4.5线程退出

void pthread_exit(void *retval);

参:代表线程的退出值。 无退出值,NULL

- 结论:
    - return:返回到调用者那里。
    - exit(): 退出当前进程。
    - pthread_exit(): 退出当前线程。

4.6线程回收

// 阻塞 回收线程。
int pthread_join(pthread_t thread, void **retval);


参1:待回收的线程id
参2:传出参数。回收的那个线程的 退出值。
    进程中:main返回值:return 0、 exit(1)  ---> int。 回收进程退出值 wait(int *)
    线程中:线程返回值:pthread_exit --> void *。 回收线程退出值 pthread_join(void **)


返回值:
    成功:0
    失败:直接返回错误号!

4.6.1回收示例1
// 子线程主题函数
void *tfn(void *arg)
{
    sleep(5);
    //return (void *)74;
    pthread_exit((void *)"hello");
}

int main(int argc, char *argv[])
{
    pthread_t tid;
    //int *retval;    // 用来存储子进程退出值
    char *retval;    // 用来存储子进程退出值

    // 创建子线程
    int ret = pthread_create(&tid, NULL, tfn, NULL);
    if (ret != 0)
        fprintf(stderr, "pthread_create err:%s\n", strerror(ret));

    printf("----------------1\n");
    // 回收子线程退出值
    ret = pthread_join(tid, (void **)&retval);
    if (ret != 0)
        fprintf(stderr, "pthread_join err:%s\n", strerror(ret));

    printf("child thread exit with %s\n", (char *)retval);

    pthread_exit((void *)0);        // 退出主线程
}
4.6.2回收示例2
struct thrd {
    int var;
    char str[256];
};

// 子线程主题函数
void *tfn(void *arg)
{
    struct thrd *tval = (struct thrd *)arg;            //malloc()
    tval->var = 100;
    strcpy(tval->str, "hello thread");

    pthread_exit((void *)tval);
    // return (void *)tval;                // 也可以
}

int main(int argc, char *argv[])
{
    pthread_t tid;
    struct thrd arg, *retval;

    // 创建子线程
    int ret = pthread_create(&tid, NULL, tfn, (void *)&arg);
    if (ret != 0)
        fprintf(stderr, "pthread_create err:%s\n", strerror(ret));

    // 回收子线程退出值
    ret = pthread_join(tid, (void **)&retval);
    if (ret != 0)
        fprintf(stderr, "pthread_join err:%s\n", strerror(ret));
    
    printf("child exit with: var = %d, str= %s\n", retval->var, retval->str);

    // free();
    
    pthread_exit((void *)0);        // 退出主线程
}

4.7线程分离

- 与进程类似,线程结束时,也有 “僵尸线程” 产生。消耗系统资源。

int pthread_detach(pthread_t thread);  // 设置线程为分离态


参:待设置为分离的线程id

- 设置为分离态的线程,在终止时,会自动清理 pcb 内核残留。
- 对于已经分离的线程,使用 pthread_join() 不能正常回收。不能获取线程退出值。

4.8杀死(取消)线程

int pthread_cancel(pthread_t thread);


参:待杀死的线程id

1. 被 pthread_cancel() 杀死的线程,在使用 pthread_join() 回收,得到的退出值 -1。 
2. pthread_cancel() 杀死线程必须要到达一个 “取消点” (保存点), 才能生效。否则无法杀死线程。
    - 应该在被cancel的线程中,调用 pthread_testcancel() 函数来添加 “取消点” (保存点)

4.8.1终止线程方法

1. return
2. pthread_exit()
3. pthread_cancel()   需要 “保存点”。 —— 进内核,即可得到。

线程进程控制原语比对:

| 线程控制原语         | 进程控制原语|
| ------------------------- | ----------------- |
| pthread_create()     | fork()             |
| pthread_self()         | getpid()          |
| pthread_exit()         | exit()              |
| pthread_join()         | wait/waitpid() |
| pthread_cancel()    | kill()                |
| pthread_detach()    |                       |
 

相关文章:

  • LeetCode热题100——滑动窗口/子串
  • dify安装
  • Linux中POSIX应用场景
  • 第4章 4.1 Entity Framework Core概述
  • 基于Java+Springboot+MySQL图书馆图书管理系统设计与实现
  • 力扣-回溯-39 组合总和
  • Python图像处理中的内存泄漏问题:原因、检测与解决方案
  • C#基础:使用Linq进行简单去重处理(DinstinctBy/反射)
  • Ubuntu 22.04 一键部署MinerU1.1.0
  • 计算机基础之操作系统——并发
  • PLSQL连接Oracle 19c报错ORA-28040
  • C语言(四)——数组
  • Redis(高阶篇)05章——案例落地实战bitmap/hyperloglog/GEO
  • 在 Windows 环境下部署 WebIssues:完整指南
  • leetcode21.合并两个有序链表
  • Python常见面试题的详解15
  • stm32hal库寻迹+蓝牙智能车(STM32F103C8T6)
  • SOME/IP--协议英文原文讲解10
  • 阿里云如何协助解决操作系统兼容性问题
  • 【小游戏】C++控制台版本俄罗斯轮盘赌
  • 海阳网站建设/企业网站建设的基本流程
  • 乐清市网站建设/上海搜索引擎优化公司排名
  • 免费建微网站平台/宁波网站推广联系方式
  • 网站建设 微信营销/网站优化排名易下拉系统
  • 做网站最好的软件是/软文广告是什么意思
  • 天津手机版建站系统价格/国外网站排名 top100