linux守护进程/线程 2019.1.8(进程组,会话,守护进程,nohup,线程)

进程组

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

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

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

 

会话

由多个进程组组成。

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

 

 

守护进程 daemon

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

 

创建守护进程模型

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

 

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

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/time.h>
#include<time.h>

//通过宏定义文件名
#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<stdio.h>
#include<pthread.h>
#include<unistd.h>

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<stdio.h>
#include<unistd.h>
#include<pthread.h>

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<stdio.h>
#include<unistd.h>
#include<pthread.h>

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<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>

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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

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<stdio.h>
#include<unistd.h>
#include<pthread.h>

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 20190108

SrcFiles=$(wildcard *.c)

TargetFiles=$(patsubst %.c,%,$(SrcFiles))

all:$(TargetFiles)

%:%.c
		gcc -o $@ $<

clean:
		-rm -f $(TargetFiles)

在 .bashrc 文件中加入这句

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

 

 

linux中的花哨技能

在 .bashrc 文件中加入这句

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值