Linux下进程与线程

进程

进程是程序的一次执行过程,并且是动态的,包括创建、调度、执行和消亡。

进程的类型

1.交互进程 2.批处理进程 3.守护进程(生命期很长)

进程状态

1.运行态 2.等待态(可中断/不可中断) 3.停止态 4.死亡态(僵尸态)(pcb没有释放)

查看进程信息

ps
ps -ef/aux|grep 关键字 // 查看系统进程快照
top
ls /proc
jobs//查看系统后台进程
bg  作业号  //将挂起的进程在后台运行(ctrl +z)
fg  作业号  //把后台的进程放到前台运行

改变进程优先级

nice -n m 可执行程序 //(m = -20 - 19):优先级 按用户指定的优先级运行进程
renice -n m pid   改变正在运行进程的优先级
//这里要注意普通用户能改变的优先级只是正数

进程创建和结束

#include <unistd.h>
pid_t fork(void);
//成功时父进程返回子进程的进程号,子进程返回0

#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
//结束当前的进程并将status的低8位的值返回
//exit()结束进程时会刷新流缓冲区_exit()不会

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);//获取子进程号
pid_t getppid(void);//获取父进程号

父子关系:
子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束 子进程变为孤儿进程和后台进程,被init进程收养
若子进程先结束 如果没有进行回收,子进程变为僵尸进程

进程创建好后,父进程从main函数运行,子进程从fork的下一条语句开始运行
父子进程不确定谁先执行,要看系统调度

使用示例1:

//执行的结果会分别返回父进程和子进程
#include <stdio.h>
#include <unistd.h>

int main(void)
{
	pid_t pid;
	if((pid = fork())<0){
		perror("fork");
		return -1;
	}
	else if(pid == 0)
		printf("child process pid=%d\n",getpid());
	else
		printf("parents process pid = %d\n",getpid());
	printf("%d\n",pid);
}

进程回收

pid_t wait(int *status);
//成功时返回子进程的进程号,失败返回EOF
//若子进程没有结束,父进程一直阻塞
//若有多个子进程,那个先结束就先回收
//status:指定保存子进程返回值和结束方式的地址
//status为NULL表示直接释放子进程PCB,不接收返回值

pid_t waitpid(pid_t pid,int *status,int option);
//成功时返回子进程pid表示成功,0表示未结束,失败EOF
//pid可用于指定回收那个子进程或任意子进程(-1)
//option指定回收方式,0或WNOHANG 阻塞或非阻塞
WIFEXITED(status);//判断子进程是否正常结束,返回逻辑真假
WEXITSTATUS(status);//获取子进程返回值
WIFSIGNALED(status);//判断子进程是否被信号结束
WTERMSIG(status);//获取结束子进程的信号类型

使用示例1:

//通过此示例可以看到status的值传入过程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
	int status;
	pid_t pid;

	if((pid = fork())<0){
		perror("fork");exit(-1);
	}
	else if(pid==0){
		sleep(1);exit(2);
	}
	else{
		wait(&status);printf("%x\n",status);//以16进制打印
	}
}

exec函数族和system

system:不用创建子进程去执行另外一个程序

#include <stdlib.h>
int system(const char *command);
//成功时返回命令command的返回值,失败时返回EOF
//当前进程等待command执行结束后才会继续执行

exec函数族:
1.进程调用exec函数执行某个程序
2.进程当前内容被指定的程序替换
3.实现让父子进程执行不同的程序

int execl(const char *path,const char *arg,..);
int execlp(const char *file,const char *arg,..);
//成功时执行指定的程序,失败时返回EOF
//path执行的程序名称,包含路径 
//arg..传递给执行的程序的参数列表  最后一个NULL
//file执行的程序的名称,在PATH中查找
int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv[]);
//成功时执行指定的程序,失败时返回EOF
//arg..封装成指针数组的形式

使用示例1:

//显示/etc下所有文件的详细信息
if(execl("/bin/ls","ls","-a","-l","/etc",NULL)<0){
	perror("execl");
}
if(execlp("ls","-a","-l","/etc",NULL)<0){
    perror("execlp");
}

使用示例2:

char *arg[] = {"ls","-a","-l","/etc",NULL};
if(execv("/bin/ls",arg)<0){
    perror("execv");
}
if(execvp("ls",arg)<0){
    perror("execvp");
}

守护进程(Daemon)

特点:始终在后台运行、独立于任何终端、周期性的执行某种任务或等待处理特定事件,linux通常以会话(session)、进程组的方式管理守护进程。通常在系统启动时运行,系统关闭时结束。

守护进程创建(1):
创建子进程,父进程退出

	if(fork()>0){
	    exit(0);
	}
子进程变为孤儿进程,被init进程收养
子进程在后台运行(但此时还依赖于终端)

守护进程创建(2):
子进程创建新会话

	if(setsid()<0){
	    exit(-1);
	}
子进程成为新的会话组长,子进程脱离原来的终端

守护进程创建(3):
更改当前的工作目录
chdir("/");//可读
chdir("/tmp");//可读可写可执行
守护进程一直在后台运行,其工作目录不能被卸载,重新设定当前 的工作目录
守护进程创建(4):
重设文件权限掩码

	if(umask(0)<0){//不会屏蔽任何权限位
	    exit(-1);
	}
掩码的设置只会影响当前进程

守护进程创建(5):
关闭打开的文件描述符(父进程打开的文件子进程所继承过来的)

	int i;
	for(i = 0;i<getdtablesize();i++){
	    close(i);
	}
已经脱离终端,stdin/stdout/stderr无法再使用

代码实现:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>

int main(){
	pid_t pid;
	FILE *fp;
	time_t t;
	int i;

	if((pid=fork())<0){
		perror("fork");exit(-1);
	}
	else if(pid>0)
		exit(0);

	setsid();//在新的会话中运行程序
	umask(0);
	chdir("/tmp");
	for(i = 0;i<getdtablesize();i++)
		close(i);
	if((fp = fopen("time.log","a")) == NULL)
	{	perror("fopen");
		exit(-1);
	}
	while(1)
	{
		time(&t);
		fprintf(fp,"%s",ctime(&t));
		fflush(fp);
		sleep(1);
	}
}

线程

线程就是轻量级进程LWP,同一个进程中的线程共享相同的地址空间,Linux不区分进程、线程,都认为是一个任务。这样大大提高了任务切换的效率,避免了额外的TLB & cache的刷新。

共享资源&私有资源

共享资源:可执行的指令、静态数据、进程中打开的文件描述符、当前工作目录、用户ID、用户组ID
私有资源:线程id(TID),PC(程序计数器)和相关寄存器、堆栈、错误号(errno)、优先级、执行状态和属性

创建线程

以下都是利用线程库来实现的:

#include <pthread.h>
int pthread_creat(pthread_t *thread,const pthread_attr_t *attr,void *(*routine)(void *),void *arg);
//成功返回0,失败返回错误码
//thread:线程对象
//attr:线程属性,NULL代表默认属性
//routine:线程执行的函数
//arg:传递给routine的参数,没有的话NULL

结束线程

#include <pthread.h>
void pthread_exit(void *retval);/return ;
//retval可被其他线程通过pthread_join获取
//线程私有资源被释放
//主进程结束,所有的线程也会结束。

回收线程

#include <pthread.h>
int pthread_join(pthread_t thread,void **retval);
//成功返回0,失败返回错误码
//thread要回收的线程对象
//调用线程阻塞直到thread结束
//*retval接收线程thread的返回值

用法示例1:

//线程创建和回收
//gcc -o pthread1 pthread1.c -lpthread 编译的时候要链接库
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>


char message[32] = "hello world";
void *thread_func(void *arg);

int main(void)
{
	pthread_t a_thread;
	void *result;

	if(pthread_create(&a_thread,NULL,thread_func,NULL)!=0){
		printf("failed to create\n");exit(-1);
	}
	pthread_join(a_thread,&result);
	printf("result if %s\n",result);
	printf("message is %s\n",message);
	return 0;
}

void *thread_func(void *arg)
{
	sleep(1);
	strcpy(message,"marked by thread");
	pthread_exit("thank you for waiting for me");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~Eliauk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值