进程和线程

一,进程

1,进程的概念

程序

存放在磁盘上的指令和数据的有序集合(文件)静态的。

进程

指行一个程序所分配的资源的总称
进程是程序的一次执行过程
动态的,包含创建,调度,执行和消亡

2,进程包含的内容

在这里插入图片描述
进程控制块,CPU寄存器值,堆栈

进程控制块(pcb)

  • 进程标识(PID)
  • 进程用户
  • 进程状态,优先级
  • 文件描述符表

进程类型

	交互进程:在shell下启动,可以在前台运行,也可以在后台运行。
	批处理进程:和终端无关,被提交一个作业队列中以便顺序执行。
	守护进程:和终端无关,一直在后台运行。

3,进程的状态

运行态:进程正在运行,或者准备运行
等待态:进程正在等待一个事件的发生或某种资源
			可中断/不可中断
停止态:进程被终止,收到信号后可以继续运行。
死亡态:已终止的进程,但pcb没有被释放。

查看进程的信息

命令描述
ps查看系统进程快照
top查看进程动态信息
/proc查看进程详细信息
nice按用户指定的优先级运行进程
renice改变正在运行进程的优先级
jobs查看后台进程
bg将挂起的进程在后台运行
fg把后台运行的程序放到前台运行

4,创建子进程

	进程创建---fork
	#include<unistd.h>
	pid_t fork(void);
	创建新的进程,失败时返回-1;
	成功时父进程返回子进程的进程号,子进程返回0
	通过fork的返回值来区分父子进程
pid_t pid;
if(pid=fork()<0){
	perror("fork");
	return -1;
}
else if(pid==0){
	printf("child process:my pid is %d\n",getpid());
}else{
	printf("parent process:my pid is %d\n",getpid());
}

5,父子进程

	1,子进程继承了父进程的内容
    2,父进程有独立的地址空间,互不影响
    3,若父进程先结束
		1)子进程成为孤儿进程,被init进程收养
		2)子进程变成孤儿进程
	4,若子进程先结束
		1)父进程如果没有及时回收,子进程变成僵尸进程。
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pid_t pid;
	int i;
	for(i=0;i<2;i++){
		pid=fork();
		if(pid<0){
			perror("fork");
			exit(-1);
		}else if(pid>0){
			printf("this is parent\n");
		}else if(pid==0){
			printf("this is child\n");
		}
}			

6,结束进程

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

7,进程回收–wait,waitpid

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

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pit_t pid;
	int i;
	int status;
	for(i=0;i<10;i++{
		pid=fork();
		if(pid<0){
			perror("fork");
			exit(-1);
		}else if(pid>0){
			printf("this is parent\n");
			wait(&status);
			printf("child is dead\n");
			while(1);
		}else if(pid==0){
			printf("this is child\n");
			sleep(2);
			exit(1);
		}
	}
}

#include<unistd.h>
pid_t waitpid(pid_t pid,int *status,int option);
成功时返回回收的子进程的pid或0,失败时返回EOF
pid可用于回收哪个子进程或任意子进程
status指定用于保存子进程返回值和结束方式抵制
option指定了回收方式,0或WNOHANG
waitpid(pid,&status,0);
waitpid(pid,&status,WNOHANG);
waitpid(-1,&status,0);
waitpid(-1,&status,WNOHANG);

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
	pid_t pid
	int i,status;
	for(i=0;i<10;i++){
		pid=fork();
		if(pid<0){
			perror("fork");
			exit(-1);
		}else if(pid>0){
			printf("this is parent\n");
			//wait(&status);
			waitpid(pid,&status,0);
			printf("child is dead ,ret=%d\n",WEXITSTATUS(status));
			while(1);
		}else if(pid==0){
			printf("this is child\n");
			sleep(2);
			exit(1);
		}
	}
}

8,进程的返回方式

子进程通过exit/_exit/return 返回某个值(0-255)
父进程调用wait(&status)回收

状态描述
WIFEXITED(&status)判断子进程是否正常结束
WEXITSTATUS(status)获取子进程返回值
WIFSIGNALED(status)判断子进程是否被信号结束
WTERMSIG(status)获取结束子进程的信号类型
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pit_t pid;
	int i,ret;
	int status;
	for(i=0;i<10;i++){
		pid=fork();
		if(pid<0){
			perror("fork");
			exit(-1);
		}else if(pid>0){
			printf("this is parent\n");
			wait(&status);
			printf("child is dead ,ret=%d\n",WEXITSTATUS(status));
			while(1);
		}else if(pid==0){
			printf("this is child\n");
			sleep(2);
			exit(1);
		}
	}
}

进程–exec函数族

  • 进程调用exec函数族来执行某个程序
  • 进程当前的内容被指定的程序替换
  • 实现让父子进程执行不同的程序
    1>父进程创建子进程
    2>子进程调用exec函数族
    3>父进程不受影响

进程–execl/execlp

#include<unistd.h>
int execl(const char *path,const char *arg,…);
int execl(const char *file,const char *arg,…);
成功时返回指定的程序,失败时返回EOF
path:执行的程序名称,包含路径
arg:传递给执行程序的参数列表
file:执行的程序的名称,在PATH中查找

#include<stdio.h>
#include<unistd.h>
int main()
{
	/*if(execl("/bin/ls","ls","-a","-l","./",NULL)<0){
		perror("execl");
	}
	*/
	if(execlp("ls","ls","-a","-l","./",NULL)<0){
		perror("execlp");
	}
	printf("i ls current dictionary\n");
}

进程–execv/execvp

#include<unistd.h>
int execv(const char *path,char *const argv[]);
int execvp(const cahr *file , char *const argv[]);
成功时执行指定的程序,失败时返回EOF
arg…封装成指针数组的形式

//执行ls命令,显示/etc目录下所有文件的详细信息
char *arg[]={"ls","-a","-l","/etc",NULL};
if(execv("/bin/ls,arg)<0){
	perror("execv");
}
if(execvp("ls",arg)<0){
	perror("execvp");
}

进程–system

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

守护进程

守护进程是Linux三种进程类型之一;
通常在系统启动时运行,系统关闭时结束;
Linux系统中大量使用,很多服务程序以守护进程方式运行;

守护进程的特点

始终在后台运行
独立与任何终端
周期性的执行某种任务或等待处理特定事件

守护进程–会话,控制终端

Linux以会话,进程组的方式管理进程。
每个进程属于一个进程组
会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会会创建一个会话,所有通过该终端运行的进程都属于这个会话。
终端关闭时,所有相关进程都会被结束。
在这里插入图片描述

守护进程的创建

  • 创建子进程,父进程退出
    if(fork()>0){
    exit(0);
    }
    子进程变成孤儿进程,被init进程收养
    子进程在后台运行
  • 子进程创建新会话
    if(setsid()<0){
    exit(-1);
    }
    子进程称为新的会话组组长
    子进程脱离原先的终端
  • 更改当前的工作目录
    chdir("/");
    chdir("/tmp");
    守护进程一直在后台运行,其工作目录不能被卸载
    重新设定当前的工作目录cwd
  • 重设文件权限
    if(umask(0)<0){
    exit(-1);
    }
    文件权限掩码设置为0
    只影响当前进程
  • 关闭打开的文件描述符
    int i;
    for(i=0;i<getdtablesize();i++){
    close(i);
    }
    关闭所有从父进程继续的打开文件
    已脱离终端,stdin/stdout/stderr无法再使用
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main()
{
	pid_t pid;
	pid=fork();
	if(pid<0){
		perror("fork");
		return -1;
	}else if(pid>0){
		exit(0);
	}
	pid=setsid();
	if(pid==-1){
		perror("setsid");
		return -1;
	}
	chdir("/");
	umask(0);
	int i;
	for(i=0;i<2;i++){
		close(i);
	}
	FILE *fp;
	time_t ctm;
	fp=fopen("1.log","w");
	while(1){
		ctm=time(NULL);
		fputs(ctime(&ctm),fp);
		fflush(fp);
		sleep(1);
	}
}

二,线程

线程概念

  • 进程在切换时系统开销大
  • 很多操作系统引入轻量级进程LWP
  • 同一进程中的线程共享相同的地址空间
  • Linux下不区分进程和线程

线程特点

  • 通常线程指的是共享相同地址空间的多个任务task_struct
  • 使用多线程的好处:
    1>大大提高任务切换的效率
    2>避免了额外的TLB&cache的刷新

线程共享资源

一个进程中多个线程共享一下资源:

  • 可执行的指令
  • 静态数据
  • 进程中打开的文件描述符
  • 当前工作目录
  • 用户ID
  • 用户组ID

线程私有资源

每个线程私有的数据包括:

  • 线程ID(TID)
  • PC(程序计数器)和相关寄存器
  • 堆栈
  • 错误号
  • 优先级
  • 执行状态和属性

Linux线程库

pthread线程库中提供了如下操作:
1>创建线程
2>回收线程
3>结束线程

同步和互斥机制
1>信号量
2>互斥锁

线程创建–pthread_create

#include<pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(routine)(void *),void *arg);
成功返回0,失败时返回错误码
thread:线程对象
attr:线程属性,NULL代表默认属性
routine:线程执行的函数
arg:传递给routine的参数,参数是void *,注意传递参数的数据格式
pthread_t pthread_self(void) :查看自己的TID

  • 创建一个线程,查看线程的tid和pid
  • 创建多个线程,查看线程的tid和pid ,立即线程和进程PCB关系。
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void *function1(void *arg){
	printf("this is thread function1\n");
	sleep(10);
}
void *function2(void *arg){
	printf("this is thread function2\n");
	while(1) sleep(1);
}

int main()
{
	pthread_t tid;
	int err;
	err=pthread_create(&tid,NULL,function1,void *arg);
	if(err!=0){
		printf("create thread:%s",strerror(err));
		return -1;
	}
	sleep(1);
	err=pthread_create(&tid,NULL,function2,void *arg);
	if(err!=0){
		printf("create thread:%s",strerror(err));
		return -1;
	}
	sleep(1);
}

线程回收–pthread_join

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

#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void *function1(void *arg){
	printf("this is thread function1\n");
	sleep(10);
}
int main()
{
	pthread_t tid[100];
	int err,i;
	for(i=0;i<100;i++){
		err=pthread_create(&tid[i],NULL,function1,NULL);
		if(err!=0){
			printf("create pthread:%s\n",strerror(err));
			return -1;
		}
	}
	sleep(5);
	for(i=0;i<100;i++){
		err=pthread_join(tid[i],NULL);
		if(err!=0){
			printf("join pthread:%s\n",strerror(err));
			return -1;
		}
	}
		while(1)  sleep(10);
}

线程结束—pthread_exit

#include<pthread.h>
void pthread_exit(void **retval);
结束当前线程
retval可被其他的线程通过pthread_join获取
线程私有资源被释放

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>

void *funct(void *arg)
{
	int ret;
	printf("this is funct pthread\n");
	sleep(1);
	ret=5
	pthread_exit((void *)ret);
}
int main()
{
	int ret i;
	pthread_t tid;
	ret=pthread_create(&tid,NULL,funct,(void *)i);
	void *retval;
	pthread_join(&tid,&retval);
	printf("retval=%s\n",(char *)retval);
}

编译:gcc -o test test.c -lpthread

pthread_detach: 使用了detach就不用join

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>

void *funct(void *arg)
{
	int ret;
	printf("this is funct pthread %d\n",(void *)arg);
	sleep(20);
//	ret=5
//	pthread_exit((void *)ret);
}
int main()
{
	int ret i;
	pthread_t tid[NUM];
	for(i=0;i<NUM;i++){
		ret=pthread_create(&tid[i],NULL,funct,(void *)i);
		pthread_detach(tid[i]);
	}
	/*
	re=pthread_join(&tid,&retval);
	if(re!=0){
		printf("join error %s\n",sterror(re));
	}
	printf("retval=%s\n",(char *)retval);
	*/
}

取消一个线程

  • int pthread_cancel(pthread_t thread);
  • void pthread_testcancel(void);
  • int pthread_setcancelstate(int state,int *oldstate);
    state:1>PTHREAD_CANCEL_ENABLE
    2>PTHREAD_CANCEL_DISABLE
  • int pthread_setcanceltype(int type, int *oldstate);
    type: 1> PTHREAD_CANCEL_DEFERRED
    2>PTHREAD_CANCEL_ASYNCHRONOUS
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>

void *funct(void *arg)
{
	int a=0;
	a=(int)arg;
	int td=pthread_self();
	pthread_detach(pthread_self());
	printf("this is thread a=%d\n",n);
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
	//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
	while(1){
		//sleep(1);
		//pthread_testcancel();
	}
}
int main()
{
	int ret i;
	pthread_t tid
	ret=pthread_create(&tid,NULL,funct,(void *)i);
	pthread_detach(tid);
	if(ret!=0){
		printf("pthread_create:%s\n",strerror(ret));
		exit(0);
	}
	sleep(5);
	//pthread_cancel();
	while(1){
		sleep(1);
	}	
}

线程间通信

  • 线程共享同一进程的地址空间
  • 优点:线程间通信通过全局变量交换数据
  • 缺点:多个线程访问共享数据时需同步或互斥机制

线程间的同步

同步机制(PV操作)
是指多个任务按照约定的先后次序完成配合完成一件事情,由信号量来决定线程是继续运行还是阻塞等待。
信号量
信号量代表某一资源,其值代表系统中该资源的数量。信号量是一个受保护的变量,只能通过三种方式来访问:初始化,P操作(申请资源),V操作(释放资源)。POSIX定义了两类信号量:无名信号量(基于内存的信号量),有名信号量。
pthread常用的信号量操作函数如下:int sem_init(sem_t *sem,int pshared,usigned int value); //信号量初始化 sem向要初始化的信号量对象,pshared指0-线程间,1-进程间,value:信号量初值。
int sem_wait(sem_t *sem);//p操作
int sem_post(sem_t *sem); //v操作
P操作
V操作

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<sempthore.h>
char buf[100];
sem_t sem;
void *write(void *arg){
	sem_t 
	int a=0;
	a=(int)arg;
	int td=pthread_self();
	pthread_detach(pthread_self());
	
	while(1){
		fgets(buf,100,stdin);
		sem_post(&sem); //申请资源,写线程,写完后释放资源,v操作
		
	}
}

void *read(void *arg){
	int a=0;
	a=(int)arg;
	int td=pthread_self();
	pthread_detach(pthread_self());

	while(1){
		memset(buf,0,100);
		sem_wait(&sem);
		printf("buf :%s\n",buf);  //消耗资源,读线程,等待写线程的资源,使用P操作
	}
}
int main(){
	int ret,i=0;
	pthread_t tid1,tid2;
	sem_init(&sem,0,0);
	re=pthread_create(&tid1,NULL,read,(void *)i);
	pthread_detach(tid);
	if(ret!=0){
		printf("pthread_create:%s\n",strerror(ret));
		exit(0);
	}
	ret=pthread_create(&tid2,NULL,write,(void *)i);
	pthread_detach(tid);
	if(ret!=0{
		printf("pthread_create :%s\n",strerror(ret));
		exit(0);
	}
	while(1){
		sleep(1);
	}
}

线程间的互斥

临界资源,互斥机制,互斥锁
临界资源:一次只允许一个任务(进程,线程)访问的共性资源
临界区:访问临界区的代码
互斥机制:mutex互斥锁,任何访问临界资源前申请锁,访问完后释放锁。
互斥锁初始化—pthread_mutex_init
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
成功时返回0,失败时返回错误码EOF
mutex指向要初始化的互斥锁对象
attr:互斥锁属性,NULL表示缺省默认属性
man函数出现NO manual entry for pthread)mutex_xxx 使用apt-get install manpages-posix-dev

申请锁–pthread_mutex_block
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex:指向要初始化的互斥锁对象
如果无法获得锁,任务阻塞

释放锁–pthread_mutex_unlock
#include<pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex指向要初始化的互斥锁对象
执行完临界区要及时释放锁

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<sempthore.h>
char buf[100];
FILE *fp;
pthread_mutex_t mutex;
void *write1(void *arg){
	char *c1="hello world";
	int len,i;
	char *c2;
	int a=0;
	a=(int)arg;
	len=strlen(c1);
	int td=pthread_self();
	pthread_detach(pthread_self());
	c2=c1;
	while(1){
		pthread_mutex_lock(&mutex);
		for(i=0;i<len;i++){
			fputc(*c1,fp);
			fflush(fp);
			c1++;
			usleep(10000);
		}
		pthread_mutex_unlock(&mutex);
		c1=c2;
		sleep(1);
	}
}
void *write2(void *arg){
	char *c2="how are you"
	int len,i;
	char *c2;
	int a=0;
	a=(int)arg;
	len=strlen(c1);
	int td=pthread_self();
	pthread_detach(pthread_self());
	while(1){
		pthread_mutex_lock(&mutex);
		for(i=0;i<len;i++){
			fputc(*c1,fp);
			fflush(fp);
			c1++;
			usleep(10000);
		}
		pthread_mutex_unlock(&mutex);
		c1=c2;
		sleep(1);
	}
}
int main(){
	int ret,i=0;
	pthread_t tid1,tid2;
	fp=fopen("1.txt","w");
	if(!fp){
		perror("fopen");
		return -1;
	}
	pthread_mutex_init(&mutex,MULL)
	re=pthread_create(&tid1,NULL,write1,(void *)i);
	pthread_detach(tid);
	if(ret!=0){
		printf("pthread_create:%s\n",strerror(ret));
		exit(0);
	}
	ret=pthread_create(&tid2,NULL,write2,(void *)i);
	pthread_detach(tid);
	if(ret!=0{
		printf("pthread_create :%s\n",strerror(ret));
		exit(0);
	}
	while(1){
		sleep(1);
	}
}

进程间通信

早期unix进程间的通信方式:无名管道(pipe),有名管道(fifo),信号(signal)。
System V IPC :共享内存,消息队列,信号灯集(进程间同步),套接字。在这里插入图片描述
1>无名管道的特点:
只能用于具有亲缘关系的进程之间的通信,单工的通信方式,具有固定的读写端。无名管道创建时会返回两个文件描述符,分别用于读写管道。
无名管道的创建–pipe
#include<unistd.h>
int pipe(int pfd[2]);
成功时返回0,失败时返回EOF
pfd包含两个元素的整型数组,用来保存文件描述符
pfd[0]用于读管道;pfd[1]用于写管道;
在这里插入图片描述

#include<unistd.h>
#include<stdio.h>
#include<string.h>
int main()
{
	int pfd[2];
	int re;
	re=pipe(pfd);
	if(re==-1){
		perror("pipe");
		return -1;
	}
	pid=fork();
	if(pid<0){
		perror("fork");
		return -1;
	}else if(pid==0){
		char buf[64]="haha,i am child.pipe message!\n";
		while(1){
			write(pfd[1],buf,strlen(buf));
			sleep(1);
		}
	}else{
		char buf[64];
		while(1){
			memset(buf,0,64);
			read(pfd[0],buf,64);
			if(re>0){
				printf(" message form child %s\n",buf);
			}else{
				break;
			}
		}
}

无名管道读特性:读无名管道:写端存在:1)有数据 2)无数据(阻塞)
写端不存在:1)有数据(不存在) 2)无数据(立即返回)
写无名管道特性:写无名管道:读端存在: 1)有空间 2)无空间(阻塞)
读端不存在:管道破裂
如何获取无名管道的大小?

#include<unistd.h>
#include<stdio.h>
#include<string.h>

int main()
{
	int pfd[2];
	pid_t pid;
	int re,i;
	re=pipe(pfd);
	if(re==-1){
		perror("pipe");
		return -1;
	}
	for(i=0;i<10000000;i++){
		write(pfd[1],"a",1);
		printf("i=%d\n".i);
	}
}

2>有名管道特点:

  • 对应管道文件,可用于任意进程之间进行通信
  • 打开管道时可指定读写方式
  • 通过文件IO操作,内容存放咋内存中
    有名管道创建–mkfifo
    #include<unistd.h>
    #include<fcntl.h>
    int mkfifo(const char *path,mode_t mode);
    成功时返回0,失败时返回EOF
    path创建的管道文件路径
    mode 管道文件的权限,如0666
fifowrite.c

#include<sys/types.h>
#inlcude<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>

int main()
{
	int re;
	int fd;
	char buf[64];
	unlink("/myfifo");
	re=mkfifo("/myfifo","0666);
	fi(re==-1){
		perror("mkfifo");
		return -1;
	}
	fd=open("/myfifo",O_WRONLY);
	if(fd<0){
		perror("open");
		return -1;
	}
	strcpy(buf,"fifo write test");
	while(1){
		write(fd,buf,strlen(buf));
		sleep(1);
	}
}
fiforead.c

#include<sys/types.h>
#inlcude<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>

int main()
{
	int re;
	int fd;
	char buf[64];
	fd=open("/myfifo",O_RDONLY);
	if(fd<0){
		perror("open");
		return -1;
	}
	while(1){
		memset(buf,0.64);
		read(fd,buf,64);
		printf("%s\n",buf);
		sleep(1);
	}
}

3>信号
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式,Linux内核通过信号通知用户进程,不同的信号代表不同的事件。
Linux对早期的unix信号机制进行扩展
进程对信号有不同的相应方式:缺省方式,忽略信号,捕捉信号
Linux下 kill -l 命令查看信号
信号相关命令kill/killall
kill [-signal] pid
默认发送SIGTERM -signal :可指定信号,pid:指定发送对象
killall [-u user|prog]
prog:指定进程名 user:指定用户名
信号发送–kill/raise
#include<unistd.h>
#include<signal.h>
int kill(pid_t pid,int sig);
int raise(int sig) //给自己发信号
成功时返回0,失败时返回EOF
pid接受进程的进程号
0代表同组进程,-1代表所有进程
sig信号类型

int alarm(unsigned int seconds);
成功时返回上个定时器的剩余时间,失败时返回EOF
seconds定时器的时间
一个进程中只能设定一个定时器,时间到时产生SIGALARM

int pause(void);
进程一直阻塞,直到被信号中断
被信号终端返回-1

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
	alarm(3);
	pause();
	printf("hello wold\n");
	return 0;
}

设置信号响应方式–signal
#include<unistd.h>
#include<signal.h>
void (*signal(int signo,void *(handler)(int)))(int);
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
成功时返回原先的信号处理函数,失败时返回SIG_ERR
signo要设置的信号类型、
handler指定的信号处理函数,SIG_DFL代表缺省方式,SIG_IGN代表忽略信号

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

void handler (int signo){
	if(signo==SIGINT){
		printf("i have got SIGINT\n");
	}
	if(signo==SIGQUIT){
		printf("I have got SIGQUIE\n");
	}
}

int main()
{
	signal(SIGINT,handler);
	signal(SIGQUIT,handler);
	while(1){
		sleep(1);
	}
	return 0;
}
//按两次CTRL+C后,中止程序
#include<stdio.h>
#include<unistd.h>
typedef void (*sight)(int)
sight oldhandle;
int count=0;

void funch(int sig)
{
	count++;
	printf("i catch ctrl+c!\n");
	if(count==2){
		signal(SIGINT,oldhandle);
	}
}
int main()
{
	oldhandle=signal(SIGINT,funch);
	while(1) sleep(1);	
}
//回收子进程僵尸
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void functh(int sig){
	wait(NULL);
}

int main()
{
	pid_t pid;
	signal(SIGCHLD,functh);
	if(pid<0){
		perror("fork");
		return -1;
	}else if(pid==0){
		printf("this is child\n");
		sleep(10);
		exit(0);
	}
	while(1){
		printf("i am working\n");
		sleep(1);
	}
}

4>System V IPC
Linux下使用ipcs命令可看到

  • IPC对象包含:共享内存,消息队列和信号灯集
  • 每个IPC对象有唯一的ID
  • IPC对象创建后一直存在,直到被显示的删除
  • 每个IPC对象有一个关联的key
  • ipcs/ipcrm

System V IPC–ftok
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char *path,int proj_id);
成功时返回合法的key值,失败时返回EOF
path存在且可访问的文件路径
proj_id用于生成key的数字,范围1-255

共享内存

  • 共享内存是一种最为高效的进程间通信的方式,进程可以直接读写内存,而不需要任何数据的拷贝
  • 共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
  • 由于多个进程可同时访问内存空间,因此需要同步和互斥机制配合使用。

共享内存使用步骤:

  • 创建/打开共享内存
  • 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
  • 读写共享内存
  • 撤销共享内存对象
  • 删除共享内存对象

共享内存创建–shmget
#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key,int size,int shmflg);
成功时返回共享内存的id,失败时返回EOF
key和共享内存关联的key,IPC_PROVATE或ftok生成
shmflg共享内存标志位IPC_CREAT|0666

共享内存映射–shmat
#include<sys/ipc.h>
#include<sys/shm.h>
void *shmat(int shmid,const void *shmaddr,int shmflg);
成功时返回映射后的地址,失败时返回EOF
shmid:要映射的共享内存id
shmaddr:映射后的地址,NULL表示由系统自动映射
shmflg: 标志位 0:表示可读写,IPC_RDONLY表示只读

共享内存撤销映射–shmdt
#include<sys/ipc.h>
#include<sys/shm.h>
int shmdt<void *shmaddr);
成功时返回0,失败时返回EOF
不使用共享内存时因撤销隐射
进程结束时自动撤销

共享内存控制–shmctl
#include<sys/ipc.h>
#include<sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
成功时返回0,失败时返回EOF
shmid :要操作的共享内存的id
cmd:要执行的操作 IPC_STAT IPC_RMID
buf:保存成设置共享内存属性的地址

write sharememory.c

#include<stdio.h>
#include<sys/ipc.h>
#include<string.h>
int main()
{
	char *addr;
	int shmid;
	key_t key;
	key=ftok(".",23);
	if(key==-1){
		perror("ftok");
		return -1;
	}
	shmid=shmget(key,1024,IPC_CREATE|0666);
	if(shmid==-1){
		perror("shmget");
		return -1;
	}
	addr=shmat(shmid,NULL,0);
	strcpy(addr,"this my share memory");
	shmdt(addr);
	
//执行后,用ipcs命令查看是否创建了共享内存
}
read sharememory.c
#include<stdio.h>
#include<sys/ipc.h>
#include<string.h>
int main()
{
	char *addr;
	int shmid;
	key_t key;
	key=ftok(".",23);
	if(key==-1){
		perror("ftok");
		return -1;
	}
	shmid=shmget(key,1024,IPC_CREATE|0666);
	if(shmid==-1){
		perror("shmget");
		return -1;
	}
	addr=shmat(shmid,NULL,0);
	printf("get share =%s\n",addr);
	shmdt(addr);
	shmctl(IPC_RMID,NULL);

4>消息队列

  • 消息对列是System V IPC对象的一种
  • 消息队列由消息队列ID来唯一标识
  • 消息队列就是一个消息的列表。用户可以在消息队列中添加消息,读取消息等
  • 消息队列可以按照类型来发送/接受消息

消息队列使用步骤

  • 打开/创建消息队列,msgget
  • 向消息队列发送消息,msgsnd
  • 从消息队列接受消息,msgrcv
  • 控制消息队列,msgctl

消息队列创建/打开–msgget
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
成功时返回消息队列的id,失败时返回EOF
key和消息队列关联的key IPC_PRIVATE和ftok
msgflg标志位IPC_CREAT|0666

消息发送–msgsnd
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msgid,const void *msgp,size_t size,int msgflg);
成功时返回0,失败时返回-1;
msgid:消息队列id
msgp:消息缓冲区地址
msgp消息结构体指针:struct msgbuf
{
long mtype; //消息类型
char metx; //消息正文
}
size:消息正文长度
msgflg:标志位0或IPC_NOWAIT

消息接受–msgrcv
#include<sys/ipc.h>
#include<sys/msg.h>
int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int msgflg);
成功时返回收到的消息长度,失败时返回-1;
msgid :消息队列id
msgp:消息缓冲区地址
size:指定接收的消息长度
msgtype:指定接收的消息类型
0:接收队列中第一个消息
大于0:接收消息队列中第一个类型为msgtype的消息
小于0: 接受消息队列中第一个类型值不小于msgtype绝对值类型第一个消息
msgflg:标志位 0或IPC_NOWAIT

控制消息队列–msgctl
#include<sys/ipc.h>
#include<sys/msg.h>
int msgctl(int msgid,int cmd,struct msgid_ds *buf);
成功时返回0,失败时返回-1;
msgid:消息队列id
cmd:要执行的操作 IPC_STAT/IPC_SET/IPC_RMID
buf:存放消息队列属性的地址

msgsnd.c

#include<sys/ipc.h>
#include<stdio.h>
#include<sys/msg.h>
#define LEN sizeof(MSG)-sizeof(long)
int main()
{
	typedef struct {
		long type;
		char txt[64];
	}MSG;
	key_t ipkey;
	int msgid;
	MSG msg_t;
	ipkey=ftok(".",23);
	if(ipkey==-1{
		printf("ftok");
		return -1;
	}
	msgid=msgget(ipkey,IPC_CREAT|0666);
	if(msgid==-1){
		perror("msgget");
		return -1;
	}
	msg_t.type=1;
	strcpy(msg_t.txt,"msg type one");
	msgsnd(msgid,&msg_t ,LEN,0);

	msg_t.type=2;
	strcpy(msg_t.txt,"msg type two");
	msgsnd(msgid,&msg_t ,LEN,0);

	msg_t.type=3;
	strcpy(msg_t.txt,"msg type three");
	msgsnd(msgid,&msg_t ,LEN,0);

	msg_t.type=4;
	strcpy(msg_t.txt,"msg type four");
	msgsnd(msgid,&msg_t ,LEN,0);


	msg_t.type=5;
	strcpy(msg_t.txt,"msg type five");
	msgsnd(msgid,&msg_t ,LEN,0);
}
msgrcv.c
#include<sys/ipc.h>
#include<stdio.h>
#include<sys/msg.h>
#include<stdlib.h>
#define LEN sizeof(MSG)-sizeof(long)

void rmmsg(int sig)
{
	msgctl(msgid,IPC_RMID,NULL);
	exit(0);
}

int main()
{
	typedef struct {
		long type;
		char txt[64];
	}MSG;
	key_t ipkey;
	int msgid;
	MSG msg_t;
	ipkey=ftok(".",23);
	if(ipkey==-1{
		printf("ftok");
		return -1;
	}
	msgid=msgget(ipkey,IPC_CREAT|0666);
	if(msgid==-1){
		perror("msgget");
		return -1;
	}
	signal(SIGINT,rmmsg);
	while(1){
		re=msgrcv(msgid,&msg_t,LEN,3,EXCEPT);  //只接收三以外的其他消息
		printf("receive msg :type=%d,txt=%s\n",msg_t.type,msg_t.txt);
		if(re<0){
			break;
		}
	}
}

5>System V IPC-信号灯
信号灯也叫信号量,用于进程/线程同步或互斥的机制
信号灯的类型
- posix无名信号灯
- posix有名信号灯
- systme V 信号灯
信号灯的含义
计数信号灯
System V IPC–信号灯特点:
system v 信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁

System v信号灯使用步骤
打开/创建信号灯   semget
信号灯初始化        semctl
P/V操作                 semop
删除信号灯            semctl

信号灯打开/创建--semget
#include<sys/ipc.h>
#include<sys/sem.h>
int semget(key_t key,int nsems,int semflg);
成功时返回信号灯的id,失败时返回-1;
key和信号队列关联key  IPC_PRIVATE或ftok
nsems 集合中包含的计数信号灯个数
semflg标志位IPC_CREAT|0666  IPC_EXCL

信号灯初始化--semctl
#include<sys/ipc.h>
#include<sys/sem.h>
int semctl(int semid,int semnum,int cmd,....);
成功时返回0,失败时返回EOF
semid  要操作的信号灯集id
semnum   要操作的集合中的信号灯编号
cmd  执行的操作  SETVAL  IPC_RMID
union  semun  取决于cmd		


信号灯P/V操作--semop
#include<sys/ipc.h>
#include<sys/sem.h>
int semop(int semid,struct sembuf *sops,int nsops);
成功时返回0,失败时返回-1;
semid:要操作的信号灯集id
sops:描述对信号灯操作的结构体(数组)
nsops:要操作的信号灯的个数

	信号灯操作--sembuf
	struct sembuf
	{
		short sem_num;
		short sem_op;
		short  sem_flg;
	};
	semnum :信号灯编号
	sem_op:   -1:P操作,1:V操作
	sem_flg:   0/IPC_NOWAIT
#include<stdio.h>
#include<stdlib.h>
#include<sys/type.h>
#include<sys/ipc.h>
#include<errno.h>
#include<string.h>
#define SEM_READ 0
#define SEM_WRITE 1
union semun{
	int val;
}

poperation(int index,int semid)
{
	struct sembuf sop;
	sop.sem_num=index;
	sop.sem_op=-1;
	sop.em_flg=0;

	semop(semid,&sop,1);
}

voperation(int index)
{
	struct sembuf sop;
	sop.sem_num=index;
	sop.sem_op=1;
	sop.em_flg=0;

	semop(semid,&sop,1);
}
int main()
{
	int semid;
	key_t key;
	int shmid;
	char *shmaddr;
	pid_t pid;
	key=ftok(".",123);
	//创建信号灯
	semid=semget(key,2,IPC_CREAT|0666);
	if(semid<0){
		perror("semget");
		return -1;
	}
	//创建共享内存
	shmid=shmget(key,256,IPC_CREAT|0666);
	if(shmid<0){
		perror("shmget");
		return -1;
	}
	//初始化2个信号灯
	nuion semun myun;
	//初始化读信号灯
	myun.val=0;
	semctl(semid,SEM_READ,SETVAL,myun);
	//初始化写信号灯
	myun.val=1;
	semctl(semid,SEM_WRITE,SETVAL,myun);

	pid=fork();
	if(pid<0){
		perror("fork");
		return -1;
	}else if(pid==0){
		shmaddr=(char *)shmat(shmid,NULL,0);
		while(1){
			poperation(SEM_READ,semid);
			printf("getshm:%s\n",shmaddr);
			voperation(SEM_WRITE,semid);
		}
	}else{
		shmaddr=(char *)shmat(shmid,NULL,0);
		while(1){
			poperation(SEM_WRITE,semid);
			printf("please input char to shm:");
			fgets(shmaddr,32,stdin);
			voperation(SEM_READ,semid);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值