linux守护进程/线程 2019.1.8(进程组,会话,守护进程,nohup,线程)
发布日期:2021-05-06 23:06:35 浏览次数:29 分类:精选文章

本文共 5798 字,大约阅读时间需要 19 分钟。

进程组

也称之为作业。进程组ID等于第一个进程ID,因此可以通过进程ID是否等于进程组ID来判断该进程是不是进程组的组长。

只要进程组中有一个进程存在,进程组就存在,哪怕组长已经没了。

一个进程可以为自己或子进程设置进程组ID

 

会话

由多个进程组组成。

创建会话要注意以下5点:

 

 

守护进程 daemon

守护进程常常以“d”结尾   

 

创建守护进程模型

创建守护进程最关键的一步就是调用setsid函数创建一个新的Session,并成为Session Leader

 

创建一个守护进程:每份中在$HOME/log/ 创建一个文件 ,文件名—— 程序名.时间戳

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//通过宏定义文件名#define _FILE_NAME_FORMAT_ "%s/log/mydaemon.%ld" //定义文件格式化void touchfile(int num){ //获取家目录 char *HomeDir=getenv("HOME"); char strFilename[250]={0}; //获取时间戳的函数 //time(NULL); sprintf(strFilename, _FILE_NAME_FORMAT_, HomeDir, time(NULL)); int fd=open(strFilename, O_RDWR|O_CREAT, 0666); //文件打开失败 if(fd<0){ perror("open err"); exit(1); } close(fd);}int main(){ //创建守护进程的步骤如下 //创建子进程,父进程退出 pid_t pid=fork(); if(pid>0){ exit(1); } //当会长 setsid(); //设置掩码 umask(0); //切换目录 //getenv获取环境变量 chdir(getenv("HOME"));//切换到家目录 //关闭文件描述符(一般通过测试之后,才会关闭文件描述符) //close(0),close(1),close(2) //执行核心逻辑 //设置一个定时器——每60秒来一次 struct itimerval myit={ {60,0},{60,0}}; setitimer(ITIMER_REAL, &myit, NULL); //注册捕获函数 struct sigaction act; act.sa_flags=0; //清空阻塞信号集 sigemptyset(&act.sa_mask); //绑定捕获函数 act.sa_handler=touchfile; //注册捕获函数 sigaction(SIGALRM, &act, NULL); while(1){ //每隔一分钟在/home/itheima/log创建文件 sleep(1); } return 0;}

 

 

 

守护进程扩展

指令nohup去执行进程就不会是收到第一个信号

通过nohup指令也可以达到守护进程的创建效果。(可以把进程输出重定向到特定的地方)

  • nohup 指令会让cmd收不到SIGHUP信号
  • & 代表后台运行

 

 

 

线程——是苦力,是真正干活的

同一个进程之间的线程,只有不是共享的。线程实际上就是一个函数,线程有自己的之行目的,任务十分明确。多线程可以更有效的利用CPU(前提是不止一个CPU)

                                      

  • 线程——最小的执行单位
  • 进程——最小的系统资源分配单位

线程和进程,在内核中都是通过clone函数实现的。从内核看,进程和线程没有区别,要看空间是否共享。

 

查看进程中线程的方法

ps -LF 进程号

 

 

线程共享的资源

 

线程不共享的资源

每个线程有自己的errno

通过如下函数可以获得错误码对应的错误信息

char *strerror(int errnum);

 

 

线程的优缺点

 

创建一个线程

  • thread 线程的ID,传出参数
  • attr 代表线程的属性
  • 第三个参数  函数指针, void *func(void*)
  • arg 线程执行函数的参数
  • 返回值  成功  0   失败  errno

编译时需要加 -lpthread

注意:线程ID在进程内是唯一的,但是在整个操作系统内部不一定是唯一的。

#include
#include
#include
void *thr(void* arg){ //无符号长整型数 %lu printf("I am a thread! pid=%d, tid=%lu\n", getpid(), pthread_self());}int main(){ pthread_t tid; pthread_create(&tid, NULL, thr, NULL); printf("I am main thread, pid=%d, tid=%lu\n", getpid(), pthread_self()); sleep(1); return 0;}

 

上面的代码存在一个问题:

就是如果主线程不睡眠,则另一个线程没有机会去执行(因为主线程打印之后就直接执行return 0了)。将上面代码中的sleep(1)换成如下语句即可:

pthread_exit(NULL);

 

 

线程退出函数

pthread_exit

注意事项:

  • 线程中使用pthread_exit 来退出线程
  • 线程中可以用 return(主控线程不行)
  • exit代表退出整个进程

 

 

线程的回收

线程回收函数,阻塞等待

int pthread_join(pthread_t thread, void **retval);
  • thread 创建的时候传出的第一个参数
  • retval 代表的传出线程的退出信息(就是返回值)
#include
#include
#include
void *thr(void *arg){ printf("I am a thread, tid=%lu\n", pthread_self()); sleep(5); printf("I am a thread, tid=%lu\n", pthread_self()); return (void*)100;}int main(){ pthread_t tid; pthread_create(&tid, NULL, thr, NULL); void *ret; //线程回收函数 pthread_join(tid, &ret); printf("ret exit with %d\n",(int)ret); pthread_exit(NULL); return 0;}

 

 

 

杀死线程

pthread_cancel

int pthread_cancel(pthread_t thread);
  • 需要传入tid
  • 返回值 失败——errno  , 成功——0

 

测试代码

#include
#include
#include
void *thr(void*arg){ while(1){ printf("I am thread, very happy! tid=%lu\n", pthread_self()); sleep(1); } return NULL;}int main(){ pthread_t tid; pthread_create(&tid, NULL, thr, NULL); sleep(5); //杀死线程 pthread_cancel(tid); void *ret; //阻塞等待回收线程 pthread_join(tid, &ret); printf("thread exit with %d\n", ret); return 0;}

 

如果把上面的代码中的thr函数中的printf动作和sleep动作注释之后,线程就杀不死了。因为pthread_cancle函数需要有一个取消点

如果你的线程函数里面实在是没有取消点,你可以加上这样的一个函数

pthread_testcancel();

通过这个函数可以强行添加一个取消点

 

 

 

线程分离

线程分离之后,这个线程就不需要你去回收了。

#include
#include
#include
#include
void *thr(void *arg){ printf("I am a thread, self=%lu\n", pthread_self()); sleep(4); printf("I am a thread, self=%lu\n", pthread_self()); return NULL;}int main(){ pthread_t tid; pthread_create(&tid, NULL, thr, NULL); //线程分离 pthread_detach(tid); sleep(5); int ret=0; //阻塞失败 if((ret=pthread_join(tid, NULL))>0){ printf("join err:%d, %s\n", ret, strerror(ret)); } return 0;}

执行了线程分离之后,pthread_join函数就回收失败了。

 

 

 

线程属性设置分离

创建那种易产生就直接是分离状的线程,不需要我们去执行detach函数来进程线程分离(因为执行detach函数还会有一些特殊情况,比如线程创建好之后很快就结束了,此时还没执行到detach函数)

#include 
#include
#include
#include
void *thr(void *arg) { printf("I am a thread \n"); return NULL;}int main() { //设置线程属性 pthread_attr_t attr; //初始化属性 pthread_attr_init(&attr); //设置线程分离属性,这样线程创建好之后就直接分开了 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置属性分离 pthread_t tid; //创建线程 第二个参数是线程属性 pthread_create(&tid,&attr,thr,NULL); int ret; //阻塞回收线程失败 if((ret = pthread_join(tid,NULL)) > 0){ printf("join err:%d,%s\n",ret,strerror(ret)); } //摧毁属性 pthread_attr_destroy(&attr); return 0;}

 

 

比较两个线程IO是否相等

int pthread_equal(pthread_t t1, pthread_t t2);

 

创建多个子线程

#include
#include
#include
void *thr(void *arg){ int num=(int)arg; printf("I am %d thread, self=%lu\n", num, pthread_self()); return (void*)(100+num);}int main(){ pthread_t tid[5]; for(int i=0; i<5; i++){ pthread_create(&tid[i], NULL, thr, (void*)i); } for(int i=0; i<5; i++){ void *ret; pthread_join(tid[i], &ret); printf("i =%d, ret=%d\n", i, (int)ret); } return 0;}

 

 

 

线程注意事项

 

 

 

线程同步

 

多线程出现数据混乱的原因

 

 

mutex相关的函数

  • restruct 约束该块内存区域队一行的数据,只能通过后面的变量进行访问和修改
  • mutex 互斥量——锁
  • attr 属性 可以不考虑,传NULL

 

给共享资源加锁

  • mutex 就是 init初始化的那个锁

如果当前未锁,成功,加锁

如果当前已锁,阻塞等待

 

摧毁锁

  • mutex 传入的锁

 

常量初始化

这么初始化之后,就不需要在用init

 

 

Makefile模板

/bin中,写一个makefile.template文件,文件内容如下:

#create by Rudy 20190108SrcFiles=$(wildcard *.c)TargetFiles=$(patsubst %.c,%,$(SrcFiles))all:$(TargetFiles)%:%.c		gcc -o $@ $

在 .bashrc 文件中加入这句

这样,执行 echomake 就可以自动生成makefile模板了

 

 

linux中的花哨技能

在 .bashrc 文件中加入这句

然后命令行就可以像vim中操作一样

就可以这么查找 gcc 文件。回车之后,就如下图了

上一篇:linux 多线程同步 2019.1.9(互斥量的使用,死锁,读写锁,条件变量,信号量,文件锁)
下一篇:Linux 进程通信 2019.1.7(pipe,fifo,mmap,匿名映射)

发表评论

最新留言

感谢大佬
[***.8.128.20]2025年04月09日 18时04分32秒