操作系统——进程

前言:学习笔记。随时修改完善。

目录

 一、进程的基本概念

1、概念

2、组成

3、分类

4、状态

二、进程的系统调用

1、命令

2、系统调用

1)fork()函数

2)exec函数族 

3)wait()/waitpid()函数

​编辑 三、进程间的通信(重点)

1、分类

2、pipe无名管道

1)特点

2)函数

3)管道的创建与关闭 

4)无名管道的使用 

 3、fifo有名管道

1)特点

2)函数

 3)有名管道的创建

4)通过fifo实现读写

4、信号

1)概念 

2)用户进程对信号的响应方式 

3)常见信号

4)signal()函数 

 5)signal()函数的使用

5、共享内存 

1)shmget()函数 

2)ftok()函数

3)shmat()函数

4)shmdt()函数 

5)shmctl()函数

6)共享内存的实现 

 7)代码

6、消息队列 

7、信号灯


 一、进程的基本概念

1、概念

正在运行的程序叫做进程。

进程是操作系统对于正在运行的程序的一种抽象。

操作系统为了执行程序,而分配的资源的总称。

进程是资源分配的基本单位,也是独立运行的基本单位。

2、组成

     代码段

     数据段

     系统数据段:pcb/寄存器/堆栈

                 pcb:存放进程的属性

                        进程的标识PID。

                        进程用户(标识进程由哪个用户创建)

                        进程的状态,优先级

                        文件描述符

3、分类

     a.交互进程:该类进程由shell控制和运行。该类进程既可在前台运行,也可在后台运行。

     b.批处理进程:该类进程不属于某个终端,它被提交到一个序列中,以便顺序执行。

     c.守护进程:该类进程在后台运行。

4、状态

     R:运行态或者就绪态

     T:暂停态

     S:可中断等待态

     D:不可中断等待态

     Z:死亡态

二、进程的系统调用

1、命令

1)ps:查看进程

2)top:动态显示进程

3)nice:按用户指定的优先级运行程序

4)renice:改变正在运行的进程的优先级

5)kill:结束进程,包括后台进程

6)bg:将挂起的进程在后台执行

7)fg:把后台运行的进程放到前台运行

2、系统调用

1)fork()函数

fork函数用于创建一个子进程,该子进程几乎拷贝了父进程的全部内容。

a.头文件

#include <sys/types.h>
#include <unistd.h>

b.函数原型

pid_t fork(void);

c.函数功能

创建子进程 

d.返回值

 >0:父进程返回子进程的PID

=0:子进程返回0

-1:error,并设置error的值

注意:

1、fork成功后,会建立一个子进程,子进程和父进程有独立的地址空间。 

2、子进程会拷贝父进程几乎所有的内容。

3、父子进程执行的顺序不确定,统一参与cpu的调度。

4、子进程结束时,必须由父进程回收子进程的资源。

2)exec函数族 

exec函数族提供了一种在进程中启动另一个程序执行的方法。

a.exec函数 

int execl(const char *pathname, const char *arg, ...
                       /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);   
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);

 b.使用

execvp()为例,执行ls -l的功能 

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

int main()
{

	pid_t pid = fork();//创建子进程
	if(-1 == pid)
	{
		perror("fork");
		return -1;
	}

	if(0 == pid)//子进程
	{
		//execl("/bin/ls", "ls","-l",NULL);//执行指定目录下的文件,例如: /bin下的 ls -l
		
		char *argv[] = {"ls","-l",NULL};
	//	execv("/bin/ls", argv);//执行ls -l

		//execlp("ls","ls","-l",NULL);//默认目录下(/bin)找ls并执行
		execvp("ls",argv);

		exit(0);
	}
	else if(pid > 0)//父进程
	{
		wait(NULL);
		printf("I am parent process.\n");
		exit(0);

	}


	return 0;
}

3)wait()

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

a.头文件

#include <sys/types.h>
#include <sys/wait.h>

b.函数原型

pid_t wait(int *wstatus);

c.函数功能

回收子进程的资源 

 d.返回值

成功,wait会返回被收集的子进程的进程ID。
如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

e.代码 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{ 
    pid_t pid = fork();//创建子进程
    if(-1 == pid)//出错处理
    {
        perror("fork");
        return -1;
    }

    if(0 == pid)//子进程
    {
        printf("I am child process.My PID is %d.My parent PID is %d\n",getpid(),getppid());
        exit(0);
    }
    else//父进程
    {
        wait(NULL);//阻塞等任意子进程结束,并回收子进程产生的资源
        printf("I am parent process.My PID is %d.My parent PID is %d\n",getpid(),getppid());
    }
    exit(0);

    return 0;
}  

运行结果

 f.(4)WIFEXITED、WIFSIGNALED、WEXITSTATUS这几个宏用来获取子进程的退出状态。

WIFEXITED宏用来判断子进程是否正常终止(return、exit、_exit退出)
WIFSIGNALED宏用来判断子进程是否非正常终止(被信号所终止)
WEXITSTATUS宏用来得到正常终止情况下的进程返回值的。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{ 
    pid_t pid = fork();//创建子进程
    if(-1 == pid)
    {
        perror("fork");
        return -1;
    }

    if(0 == pid)
    {
        printf("I am child process.My PID is %d.My parent PID is %d\n",getpid(),getppid());
        exit(0);//结束子进程
    }
    else//父进程
    {
        int status;
        wait(&status);
        printf("exit status:%d\n",WEXITSTATUS(status));//返回子程序的退出状态
        printf("end status:%d\n",WIFEXITED(status));//获取子进程是否正常结束
        printf("end number:%d\n",WTERMSIG(status));//返回导致子进程终止的信号编码
       // while(waitpid(pid,NULL,WNOHANG) == 0)
        printf("I am parent process.My PID is %d.My parent PID is %d\n",getpid(),getppid());
    }
    exit(0);

    return 0;
} 

运行结果: 

 

 

4)waitpid()

(1) 基本功能和wait一样,都是用来回收子进程
(2) waitpid可以回收指定PID的子进程
(3) waitpid可以阻塞式或非阻塞式两种工作模式 

a.函数原型

 pid_t waitpid(pid_t pid, int *wstatus, int options);

b.函数参数

wstatus:用来接收子进程返回的状态值

options:WNOHANG(非阻塞等待);0(阻塞式等待,等价wait(NULL).) 

pid:

<-1:表示等待进程组ID等于pid绝对值的子进程。
-1:表示等待任何子进程。
0:表示等待进程组号与调用进程组号相等的子进程。
>0:表示等待进程号等于pid值的子进程。

 c.代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{ 
    pid_t pid = fork();//创建子进程
    if(-1 == pid)//出错处理
    {
        perror("fork");
        return -1;
    }

    if(0 == pid)//子进程
    {
        printf("I am child process.My PID is %d.My parent PID is %d\n",getpid(),getppid());
        exit(0);
    }
    else//父进程
    {
        waitpid(pid,NULL,WNOHANG);//非阻塞回收进程号为pid的子进程结束并回收子进程的资源
        printf("I am parent process.My PID is %d.My parent PID is %d\n",getpid(),getppid());
    }
    exit(0);

    return 0;
}  

运行结果:

 

 

 三、进程间的通信(重点)

1、分类

1)早期的进程间通信

     pipe:无名管道

     fifo:有名管道

     信号:

2)System V的IPC通信

     消息队列:
     共享内存:效率最高的通信方式
     信号灯集:

3)BSD:套接字通信
        socket(网络)

2、pipe无名管道

1)特点

a.只能用于具有亲缘关系的进程之间的通信。
b.半双工的通信模式,具有固定的读端和写端。
c.管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数。
d.无名管道并不是普通的文件,不属于任何文件系统,并且只存在于内存中。

2)函数

#include <unistd.h>
int pipe(int pipefd[2]);

3)管道的创建与关闭 

管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道。构成了一个半双工的通道。 

4)无名管道的使用 

#include<stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#define N 64

int main()
{
	int fd[2];
	int ret = pipe(fd);//创建无名管道
	if(-1 == ret)
	{
		perror("pipe");
		return -1;
	}

	pid_t pid = fork();//创建子进程
	if(-1 == pid)
	{
		perror("fork");
		return -1;
	}

	if(0 == pid)//子进程
	{
		char buf[] = "I am child process.";

		close(fd[0]);//关闭读端

		if(-1 ==  write(fd[1],buf,sizeof(buf)))//写管道
		{
			perror("write");
			close(fd[1]);
			exit(-1);

		}

		close(fd[1]);
		exit(0);
	}
	else //父进程
	{

		char buf[N] = {0};
		
		close(fd[1]);//关闭写端
		
		wait(NULL);

		if(-1 == read(fd[0],buf,sizeof(buf)))//读管道
		{
			perror("read");
			close(fd[0]);
			exit(-1);
		}
		printf("buf:%s\n",buf);

		close(fd[0]);
		exit(0);
	}



	return 0;
}

运行结果: 

 3、fifo有名管道

1)特点

a.有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见
b.进程通过文件IO来操作有名管道

c.有名管道遵循先进先出规则
d.不支持如lseek() 操作 

2)函数

int mkfifo(const char*filename,mode_t mode)

filename:要创建的管道
mode:指定创建的管道的访问权限,一般用8进制数表示 

 3)有名管道的创建

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
	int ret = mkfifo("myfifo",0777);//创建有名管道
	if(-1 == ret)
	{
		perror("mkfifo");
		return -1;

	}
	printf("mkfifo success.\n");

	return 0;
}

 运行结果:

4)通过fifo实现读写

writer.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define N 64
int main(int argc, char *argv[])
{
    char buf[N]={0};
    int fd = open("myfifo",O_WRONLY);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }

    do
    {
        fgets(buf,sizeof(buf)-1,stdin);//读标准输入
        write(fd,buf,sizeof(buf));
    }while(strncmp(buf,"quit",4) != 0);

    close(fd);

    return 0;
} 

reader.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define N 64
int main(int argc, char *argv[])
{
    char buf[N]={0};
    int fd = open("myfifo",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }

    while(1)
    {
        int ret = read(fd,buf,sizeof(buf));//
        if(-1 == ret)
        {
            perror("read");
            close(fd);
            return -1;
        }
        if(strncmp(buf,"quit",4) == 0)
            break;
        printf("%s\n",buf);
    }

    close(fd);

    return 0;
} 

编译成可执行文件: 

打开两个终端,运行:

5)注意

1)当管道中没有数据时,读管道阻塞
2)当写端关闭时,读端读到0
3)当读端关闭时,写端继续写,就会造成管道破裂,系统发送信号SIGPIPE信号给写进程

4、信号

1)概念 

 a.信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式.
 b.主要用于用户进程和内核进程进行交互.

2)用户进程对信号的响应方式 

1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。

3)执行默认操作:Linux对每种信号都规定了默认操作 

3)常见信号

SIGKILL: 终止进程
SIGALRM:闹钟信号 当定时器时间到时就会收到此信号
SIGSTOP: 暂停
SIGCHLD:子进程结束时父进程收到此信号, 默认:忽略
SIGPIPE:管道破裂 默认:终止

4)signal()函数 

功能:将信号和处理函数关联 

#include <signal.h>

typedef void (*sighandler_t)(int);//函数声明

sighandler_t signal(int signum, sighandler_t handler);

 5)signal()函数的使用

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

void handler(int signo)//回调函数
{
	if(signo == SIGALRM)
		printf("time out.\n");

}

int main()
{
	signal(SIGALRM,handler);//信号和处理函数关联

	printf("%d\n",alarm(2));//返回上个alarm剩余的时间

	pause();//将进程挂起 直到收到信号

	while(1)
	{
		printf("hello\n");
		sleep(1);
	}




	return 0;
}

 运行结果:

5、共享内存 

共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数
据的拷贝。

1)shmget()函数 

a.头文件

#include <sys/ipc.h>
#include <sys/shm.h>

b.函数原型

int shmget(key_t key, size_t size, int shmflg);

函数参数:

key: IPC_PRIVATE 或 ftok()获取

size:共享内存大小

shmflg: IPC_CREAT 不存在则创建

c.函数功能

创建/打开共享内存 

d.返回值

成功:共享内存的id.
失败:-1 设置errno.

2)ftok()函数

a.头文件

#include <sys/types.h>
#include <sys/ipc.h>

b.函数原型

key_t ftok(const char *pathname, int proj_id);

c.函数参数

const char *pathname:文件路径名

int proj_id:子序号

 d.返回值

成功:返回生成的key值

失败:返回-1

3)shmat()函数

共享内存映射到私有地址空间

a.头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

b.函数原型

void *shmat(int shmid, const void *shmaddr, int shmflg);

c.函数参数

shmid: shmget的返回值
shmaddr:NULL 系统自己去映射到私有地址空间
shmflg: 0 共享内存可读可写

d.返回值

成功:映射的首地址。

失败:(void *)-1,设置errno.

4)shmdt()函数 

解除映射。

a.头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

b.函数原型

int shmdt(const void *shmaddr);

c.函数参数

 shmaddr:shmat的返回值。

d.返回值

成功:0
失败:-1 设置errno

5)shmctl()函数

 删除共享内存

a.头文件 

#include <sys/ipc.h>
#include <sys/shm.h>

b.函数原型

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

c.函数参数

 shmid: shmget的返回值
 cmd:IPC_RMID  删除共享内存
 buf: NULL

d.返回值

成功:0
失败:-1 设置errno

6)共享内存的实现 

a.创建/打开共享内存。shmget

b.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问。shmat

c.撤销共享内存映射。shmdt

d.删除共享内存对象。shmctl

 7)代码

共享内存实现读和写。

writer.c

#include<stdio.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>

#define SIZE 64

int main()
{
	key_t key = ftok(".",'s');//获取key
	if(-1 == key)
	{
		perror("ftok");
		return -1;
	}
	//1.创建共享内存 若存在则打开
	int shmid = shmget(key,SIZE,IPC_CREAT | 0777);
	if(-1 == shmid)
	{
		perror("shmget");
		return -1;
	}
	printf("shmid=%d\n",shmid);
	//2.共享内存映射
	char *p = (char *)shmat(shmid,NULL,0);
	if((char *)-1 == p)
	{
		perror("shmat");
		return -1;
	}


	//循环读标准输入,并将读取的内容写入共享内存,直到输入"quit"结束
	char buf[SIZE] = {0};
	do{
		fgets(buf,sizeof(buf)-1,stdin);
		buf[strlen(buf)-1] = '\0';//处理'\n'

		strcpy(p, buf);//写共享内存

	}while(strncmp(buf,"quit",4) != 0);

	//3.解映射
	if(-1 == shmdt(p))
	{
		perror("shmdt");
		return -1;
	}
	return 0;
}

reader.c

#include<stdio.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>

#define SIZE 64

int main()
{
	key_t key = ftok(".",'s');//获取key
	if(-1 == key)
	{
		perror("ftok");
		return -1;
	}
	//1.创建共享内存 若存在则打开
	int shmid = shmget(key,SIZE,IPC_CREAT | 0777);
	if(-1 == shmid)
	{
		perror("shmget");
		return -1;
	}
	printf("shmid=%d\n",shmid);
	//2.共享内存映射
	char *p = (char *)shmat(shmid,NULL,0);
	if((char *)-1 == p)
	{
		perror("shmat");
		return -1;
	}


	//循环读标准输入,并将读取的内容写入共享内存,直到输入"quit"结束
	while(1)
	{
		puts(p);//读共享内存
		if(strncmp(p,"quit",4) == 0)
			break;
		sleep(1);
	}

	//3.解映射
	if(-1 == shmdt(p))
	{
		perror("shmdt");
		return -1;
	}

	//4.删除共享内存
	if(-1 == shmctl(shmid,IPC_RMID,NULL))
	{
		perror("shmctl");
		return -1;
	}
	

	return 0;
}

编译成可执行文件,打开两个终端运行

reader会一直打印共享内存里的内容,未在writer端输入内容时,共享内存没有内容,所以一直换行,直到writer端输入内容。 

6、消息队列 

1.步骤
    msgget();//创建或打开消息队列
    msgsnd();//添加消息到消息队列
    msgrcv();//读取消息从消息队列
    msgctl();//控制消息队列

7、信号灯

1.System V IPC 的信号灯
    指:一个或多个信号灯的集合;
2.函数
    semget();
    semop();
    semctl();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值