Linux系统编程——守护进程(1)【线程】
5.5案例:定时创建文件
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<time.h>
#include<unistd.h>
#define SIZE 32
//守护进程模型
int main()
{
pid_t pid = -1;
time_t t = -1;
struct tm *pT = NULL;
char file_name[SIZE];
//1.创建子进程,父进程退出
pid = fork();
if(-1 == pid)
{
perror("fork");
return 1;
}
if(pid > 0)
{
//父进程退出
exit(0);
}
//2.在子进程中创建新会话,完全脱离工作目录
pid = setsid();
if(-1 == pid)
{
perror("setsid");
return 1;
}
//6.开始执行守护进程核心工作
//每隔两秒钟输出当前的时间到/tmp/txt.log文件中
while(1)
{
sleep(1);
t= time(NULL);
//获取当前时间
// printf("ctime:%s",ctime(&t));
// 转化为时间
pT = localtime(&t);
if(NULL == pT)
{
perror("localtime");
return 1;
}
//转化为文件
memset(file_name,0,SIZE);
//+1900是因为年从1900开始计算,加1是因为月从0开始
//在用户目录下创建文件,不然没有权限
sprintf(file_name, "%s%d%d%d%d%d%d.log","touch /home/xcc/test/",pT->tm_year+1900,pT->tm_mon+1,pT->tm_mday,pT->tm_hour,pT->tm_min,pT->tm_sec);
system(file_name);
}
return 0;
}
06. 线程简介
6.1 线程概念
在许多经典的操作系统教科书中,总是把进程定义为程序的执行实例,它并不执行什么, 只是维护应用程序所需的各种资源,而线程则是真正的执行实体。
所以,线程是轻量级的进程(LWP:light weight process),在Linux环境下线程的本质仍是进程。
为了让进程完成一定的工作,进程必须至少包含一个线程。
进程,直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源,所以我们也说,进程是CPU分配资源的最小单位。
线程存在与进程当中(进程可以认为是线程的容器),是操作系统调度执行的最小单位。说通俗点,线程就是干活的。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
如果说进程是一个资源管家,负责从主人那里要资源的话,那么线程就是干活的苦力。一个管家必须完成一项工作,就需要最少一个苦力,也就是说,一个进程最少包含一个线程,也可以包含多个线程。苦力要干活,就需要依托于管家,所以说一个线程,必须属于某一个进程。
进程有自己的地址空间,线程使用进程的地址空间,也就是说,进程里的资源,线程都是有权访问的,比如说堆啊,栈啊,静态存储区什么的。
进程是操作系统分配资源的最小单位
线程是操作系统调度的最小单位
6.2 线程函数列表安装
命令:
sudo apt-get install manpages-posix-dev
【说明】manpages-posix-dev 包含 POSIX 的 header files 和 library calls 的用法
查看:
man -k pthread
6.3 NPTL
当 Linux 最初开发时,在内核中并不能真正支持线程。但是它的确可以通过 clone() 系统调用将进程作为可调度的实体。这个调用创建了调用进程(calling process)的一个拷贝,这个拷贝与调用进程共享相同的地址空间。LinuxThreads 项目使用这个调用来完全在用户空间模拟对线程的支持。不幸的是,这种方法有一些缺点,尤其是在信号处理、调度和进程间同步原语方面都存在问题。另外,这个线程模型也不符合 POSIX 的要求。
要改进 LinuxThreads,非常明显我们需要内核的支持,并且需要重写线程库。有两个相互竞争的项目开始来满足这些要求。一个包括 IBM 的开发人员的团队开展了 NGPT(Next-Generation POSIX Threads)项目。同时,Red Hat 的一些开发人员开展了 NPTL 项目。NGPT 在 2003 年中期被放弃了,把这个领域完全留给了 NPTL。
NPTL,或称为 Native POSIX Thread Library,是 Linux 线程的一个新实现,它克服了 LinuxThreads 的缺点,同时也符合 POSIX 的需求。与 LinuxThreads 相比,它在性能和稳定性方面都提供了重大的改进。
查看当前pthread库版本:getconf GNU_LIBPTHREAD_VERSION
6.4 线程的特点
类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。
因此在这类系统中,进程和线程关系密切:
-
线程是轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone
-
从内核里看进程和线程是一样的,都有各自不同的PCB.
-
进程可以蜕变成线程
-
在linux下,线程最是小的执行单位;进程是最小的分配资源单位
查看指定进程的LWP号:
ps -Lf pid
实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数 clone 。
Ø 如果复制对方的地址空间,那么就产出一个“进程”;==(深拷贝)==
Ø 如果共享对方的地址空间,就产生一个“线程”。==(浅拷贝)==
Linux内核是不区分进程和线程的, 只在用户层面上进行区分。所以,线程所有操作函数 pthread_* 是库函数,而非系统调用。
6.5 线程共享资源
-
文件描述符表
-
每种信号的处理方式
-
当前工作目录
-
用户ID和组ID
内存地址空间 (.text/.data/.bss/heap/共享库)
int num = 100;
//线程处理函数
void *fun(void *arg)
{
int *p = (int*)(arg);
printf("befor fun num = %d *p = %d\n",num,*p);
num++;
(*p)++;
printf("after fun num = %d *p = %d\n",num,*p);
return NULL;
}
//验证线程共享数据段和堆
int main()
{
int ret = -1;
pthread_t tid;
memset(&tid,0,sizeof(tid));
//分配堆空间
int *p = malloc(sizeof(int));
if(p == NULL)
{
printf("malloc failed..\n");
return 1;
}
memset(p,0,sizeof(int));
*p = 10;
//创建一个线程
ret = pthread_create(&tid,NULL,fun,(void*)p);
if(ret != 0)
{
printf("pthread_create failed...\n");
}
sleep(1);
printf("main num = %d *p = %d\n",num,*p);
return 0;
}
运行结果:
xcc@ubuntu:~/8th$ ./a.out
befor fun num = 100 *p = 10
after fun num = 101 *p = 11
main num = 101 *p = 11
6.6 线程非共享资源
-
线程id
-
处理器现场和栈指针(内核栈)
-
独立的栈空间(用户空间栈)
-
errno变量
-
信号屏蔽字
-
调度优先级
- 点赞
- 收藏
- 关注作者
评论(0)