在UNIX系统中,用户通过终端登录系统后得到一个shell进程,这个终端称为shell进程的控制终端(Controlling Terminal),进程中,控制终端是保存在PCB中的信息,而fork()会复制PCB中的信息,因此由shell进程启动的其他进程的控制终端也是这个终端
默认情况下(没有重定向),每个进程的标准输入、输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上
在控制终端输入一些特殊的控制键可以给前台进程发信号,例如Ctrl + C会产生SIGINT信号,Ctrl + \会产生SIGQUIT信号
echo $$
可以通过上述命令来查看当前终端的进程号
tty
可以用于显示终端机连接标准输入设备的文件名称
进程组和会话在进程之间形成了一种两级层次关系:进程组是一组相关进程的集合,会话是一组相关进程组的集合。进程组和会话是为支持shell作业控制而定义的抽象概念,用户通过shell能够交互式地在前台或后台运行命令
进程组由一个或多个共享同一进程组标识符(PGID)的进程组成。一个进程组拥有一个进程组首进程,该进程是创建该组的进程,其进程ID为该进程组的ID,新进程会继承其父进程所属的进程组ID
进程组拥有一个生命周期,其开始时间为首进程创建组的时刻,结束时间为最后一个成员进程退出组的时刻,一个进程可能会因为终止而退出进程组,也可能会因为加入了另外一个进程组而退出进程组。进程组首进程无需是最后一个离开进程组的成员
会话是一组进程组的集合。会话首进程是创建该新会话的进程,其进程ID会成为会话ID。新进程会继承其父进程的会话ID
一个会话中的所有进程共享单个控制终端。控制终端会在会话首进程首次打开一个终端设备时被建立,一个终端最多可能会成为一个会话的控制终端
在任一时刻,会话中的其中一个进程组会成为终端的前台进程组,其他进程组会成为后台进程组。只有前台进程组中的进程才能从控制终端中读取输入。当用户在控制终端中输入终端字符生成信号后,该信号会被发送到前台进程组中的所有成员
当控制终端的连接建立起来之后,会话首进程会成为该终端的控制进程
守护进程,也就是通常说的Daemon进程(精灵进程),是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字
守护进程具备下列特征:
Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等
/*
写一个守护进程,每隔2s获取一下系统时间,将这个时间写入到磁盘文件中
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void work(int num){
//捕捉到信号之后,获取系统时间,写入磁盘文件
time_t tm = time(NULL);
struct tm * loc = localtime(&tm);
//方法一
// char buf[1024];
// sprintf(buf,"%d-%d-%d %d:%d:%d\n",loc->tm_year,loc->tm_mon,loc->tm_mday,loc->tm_hour,loc->tm_min,loc->tm_sec);
// printf("%s\n",buf);
//方法二
char * str = asctime(loc);
int fd = open("time.txt",O_RDWR | O_CREAT|O_APPEND,0664);
write(fd,str,strlen(str));
close(fd);
}
int main(){
//1.创建子进程,退出父进程
pid_t pid = fork();
if(pid > 0){
exit(0);
}
//2.将子进程重新创建一个会话
setsid();
//3.设置掩码
umask(022);
//4.更改工作目录
chdir("/home/yuuki/lesson/lesson28/");
//5.关闭、重定向文件描述符
// int fd = open("/dev/null",O_RDWR);
// dup2(fd,STDIN_FILENO);
// dup2(fd,STDOUT_FILENO);
// dup2(fd,STDERR_FILENO);
//6.业务逻辑
//捕捉定时信号
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = work;
sigemptyset(&act.sa_mask);
sigaction(SIGALRM,&act,NULL);
struct itimerval val;
val.it_interval.tv_sec = 2;
val.it_interval.tv_usec = 0;
val.it_value.tv_sec = 2;
val.it_value.tv_usec = 0;
//创建定时器
setitimer(ITIMER_REAL,&val,NULL);
while(1){}
return 0;
}
输出的结果可以在time.txt中查看,vim里可以使用下面代码来刷新文本
:e
与进程(process)类似,线程(thread)是允许应用程序并发执行多个任务的一种机制。一个进程可以包含多个线程。同一个程序中的所有线程均会独立执行相同程序,且共享同一份全局内部区域,其中包括初始化数据段、未初始化数据段,以及堆内存段(传统意义上的UNIX进程只是多线程程序的一个特例,该进程只包含一个线程)
进程是CPU分配资源的最小单位,线程是操作系统调度执行的最小单位
线程是轻量级的进程(LWP:Light Weight Process),在Linux环境下线程的本质仍是进程
查看指定进程的LWP号:ps -Lf pid
进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换
调用fork()来创建进程的代价相对较高,即便利用写时复制技术,仍需要赋值诸如内存页表和文件描述符表之类的多种进程属性,这意味着fork()调用在时间上的开销依然不菲
线程之间能够方便、快速地共享信息。只需将数据赋值到共享变量中即可
创建线程比创建进程通常要快10倍甚至更多。线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表
在编译时最后加上-l 以及库名,如下,就能引用对应库
-lpthread
一般情况下,main函数所在的线程我们称之为主线程(main线程),其余创建的线程称之为子线程
程序中默认只有一个进程,fork()函数调用,2进程
程序中默认只有一个线程,pthread_create()函数调用,2个线程
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
#include
#include
#include
#include
void *callback(void * arg){
printf("child thread...\n");
printf("arg value : %d\n",*(int*)arg);
return NULL;
}
int main(){
pthread_t tid;
int num = 10;
//创建一个子线程
int ret = pthread_create(&tid,NULL,callback,(void*)&num);
if(ret != 0){
char * errstr = strerror(ret);
printf("error : %s\n",errstr);
}
for(int i = 0;i < 5;i++){
printf("%d\n",i);
}
sleep(1);
return 0;
}
#include
void pthread_exit(void *retval);
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1,pthread_t t2);
#include
#include
#include
void * callback(void* arg){
printf("child thread id: %ld\n",pthread_self());
return NULL;
}
int main(){
pthread_t tid;
//创建一个子线程
int ret = pthread_create(&tid,NULL,callback,NULL);
if(ret != 0){
char* errstr = strerror (ret);
printf("error : %s\n",errstr);
}
//主线程
for(int i = 0;i < 5;i++){
printf("%d\n",i);
}
printf("tid : %ld,main thread id: %ld\n",tid,pthread_self());
//让主线程退出,当主线程退出时,不会影响其他正常运行的线程
pthread_exit(NULL);
//主线程结束后,接下来的代码不会执行
printf("main thread\n");
return 0;
}
#include
int pthread_join(pthread_t thread, void **retval);
#include
#include
#include
#include
int value =10;
void * callback(void* arg){
printf("child thread id: %ld\n",pthread_self());
//sleep(2);
//return NULL;
//int value =10;
pthread_exit((void*)&value);//等同于return (void*)&value;
}
int main(){
pthread_t tid;
//创建一个子线程
int ret = pthread_create(&tid,NULL,callback,NULL);
if(ret != 0){
char* errstr = strerror (ret);
printf("error : %s\n",errstr);
}
//主线程
for(int i = 0;i < 5;i++){
printf("%d\n",i);
}
printf("tid : %ld,main thread id: %ld\n",tid,pthread_self());
//主线程调用pthread_join()回收子线程的资源
int * thread_retval;
ret = pthread_join(tid,(void**)&thread_retval);
if(ret != 0){
char* errstr = strerror (ret);
printf("error : %s\n",errstr);
}
printf("exit data : %d\n",*thread_retval);
printf("回收子线程资源成功\n");
//让主线程退出,当主线程退出时,不会影响其他正常运行的线程
pthread_exit(NULL);
return 0;
}
#include
int pthread_detach(pthread_t thread);
#include
#include
#include
#include
void * callback(void *arg){
printf("child thread id:%ld\n",pthread_self());
return NULL;
}
int main(){
//创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid,NULL,callback,NULL);
if(ret != 0){
char * errstr = strerror(ret);
printf("error1 : %s\n",errstr);
}
//输出主线程和子线程的ID
printf("tid : %ld,main thread id : %ld\n",tid,pthread_self());
//设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放
ret = pthread_detach(tid);
if(ret != 0){
char * errstr = strerror(ret);
printf("error2 : %s\n",errstr);
}
//设置分离后,对分离的子线程进行连接pthread_join()
// ret = pthread_join(tid,NULL);
// if(ret != 0){
// char * errstr = strerror(ret);
// printf("error3 : %s\n",errstr);
// }
//会报错error3 : Invalid argument
pthread_exit(NULL);
return 0;
}
#include
int pthread_cancel(pthread_t thread);
#include
#include
#include
#include
void * callback(void *arg){
printf("child thread id:%ld\n",pthread_self());
for(int i = 0;i < 5;i++){
printf("child %d\n",i);
}
return NULL;
}
int main(){
//创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid,NULL,callback,NULL);
if(ret != 0){
char * errstr = strerror(ret);
printf("error1 : %s\n",errstr);
}
//取消线程
ret = pthread_cancel(tid);
if(ret != 0){
char * errstr = strerror(ret);
printf("error2 : %s\n",errstr);
}
for(int i = 0;i < 5;i++){
printf("%d\n",i);
}
//输出主线程和子线程的ID
printf("tid : %ld,main thread id : %ld\n",tid,pthread_self());
pthread_exit(NULL);
return 0;
}
int pthread_attr_init (pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
#include
#include
#include
#include
void * callback(void *arg){
printf("child thread id:%ld\n",pthread_self());
return NULL;
}
int main(){
//创建一个线程属性变量
pthread_attr_t attr;
//初始化属性变量
pthread_attr_init(&attr);
//设置属性
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
//获取线程的栈的大小
size_t size;
pthread_attr_getstacksize(&attr,&size);
printf("thread stack size : %ld\n",size);
//创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid,&attr,callback,NULL);
if(ret != 0){
char * errstr = strerror(ret);
printf("error1 : %s\n",errstr);
}
//输出主线程和子线程的ID
printf("tid : %ld,main thread id : %ld\n",tid,pthread_self());
//释放线程属性资源
pthread_attr_destroy(&attr);
pthread_exit(NULL);
return 0;
}