进程的定义:
进程是一个具有独立功能的程序的一次性活动。
特点:
1,独立性 2,并发性 3,动态性 4, 异步性
进程ID:标识进程的唯一数字(PID以及PPID)。
进程互斥:若干个进程访问一个资源时,只允许一个进程访问资源。其他进程需等待该进程访问完毕后才能使用该资源。
临界资源:只允许一个进程访问的资源叫做临界资源。其中进程访问临界资源那段程序代码叫做临界区。
进程同步:一组并发进程按一定顺序执行并访问资源。
进程调度:按照一定的算法,从待运行的进程中选一个供CPU允许。
调度方式有抢占式和非抢占式。抢占式类似中断优先级,当中断优先级高的在运行时,低优先级阻塞。当中断优先级低的运行是,优先级高的进程要访问资源时,低优先级的进程退出。非抢占式是当A进程在访问资源时,只有等A进程访问结束,B进程才能访问,与优先级无关。
调度算法:
1 先来先服务
2 短进程优先 (就是那个访问资源占用CPU时间少的优先)
3 高优先级优先
4 时间片轮(每个进程被分配一个时间片)
死锁:多个进程竞争一个资源导致各个进程僵持不能前进。
进程编程以及库函数
1 #include <sys/types.h>
#include <unistd.h>
pid_t getpid();//获取本进程ID。
pid_t getppid();//获取父进程ID。
2 #include <unistd.h>
pid_t fork();//创建子进程
pid_t vfork();//创建子进程
fork()与vfork()的区别:
(1)fork()创建的子进程在运行的时候父子进程谁先运行未知而vfork()一定是子进程更先运行父进程后运行。
(2) vfork()共享程序的数据段,而fork()是copy程序的数据段。
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t pid;
int count=0;
pid = fork();
count++;
printf( “count = %d\n", count );
return 0;
}
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t pid;
int count=0;
pid = vfork();
count++;
printf( “count = %d\n", count );
return 0;
}
程序1执行结果 count=1 count=1 程序二执行结果 count=1 count=2
2 exec函数族
exec用被执行的程序替换调用它的程序,不产生新的pid。
(1)#include<unistd.h>
int execl(const char * path,const char * arg1, ....)
path: 被执行程序名。需要完整路径
argn:执行程序所需命令含参数,当NULL时结束。
(2) #include<unistd.h>int execlp(const char * path,const char * arg1, …)
path: 被执行程序名。不需要完整路径。
argn:执行程序所需命令含参数,当NULL时结束。
例子
#include<unistd.h>main()
{
execlp(”ls”,"ls”,"-al”,"/etc/passwd”,(char *)0);
}
(3) #include<unistd.h>
int execv (const char * path, char * const argv[ ])
path: 被执行程序名。需要完整路径
argv[]:被执行程序所需命令行参数数组
(4)#include<unistd.h>
int system(char* string)
调用fork()由子进程调用/bin/sh -c string来执行参数string所代表
的命令。
(5)#include <sys/types.h>
#include <sys/wait.h>
pid_t wait (int * status)
阻塞该进程,使其子进程退出时解除阻塞。
一般用法:wait(NULL);
二,进程通信
进场通信的目的:数据传输,实时控制,数据共享,通知事件。
进程通信分类:
1,管道
2,信号
3,消息队列
4,共享内存
5,信号量,
6,套接字(socket)。
1 管道通信
(1)无名管道
无名管道只能进行父子进程之间的数据传输。
创建无名管道函数:int pipe(int filedis[2]);其中filedis[0]是用于从管道头读数据,filedis[1]用于管道尾写数据。
注意:从管道头 读完数据后数据会自动从管道中删除。close(filedis[0]) close(filedis[1]) 管道关闭。
无名管道通信时,必须先创建一个管道,然后再创建父子进程,这样该子进程会继承父进程从管道读写数据。
例子 #include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<stdlib.h>
#include<sys/types.h>
int main()
{
int fd[2];
pid_t pid;
char buf[100];
pipe(fd);
pid=fork();
if(pid<0)
{
printf("error\n");
exit(1);
}
else if(pid==0)
{
close(fd[1]);
read(fd,buf,100);
sleep(2);
printf("%s read from files\n",buf);
close(fd[0]);
exit(0);
}
else
{
close(fd[0]);
memset(buf,0,100);
write(fd,"hello pipe",10);
wait(NULL);
close(fd[1]);
exit(0);
}
return 0;
}
2 有名管道
有名管道要创建一个FIFO文件。
创建FIFO文件的函数
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)
pathname : FIFO文件名
mode: 属性
被阻塞标志;O_NONBLOCK 访问要求无法满足书立刻返回错误,errno是ENXIO。
例子:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "/software/hello"
int main(int argc char *argv[])
{
int fd,n;
char buf[100];
if(mkfifo(FIFO,O_CREAT|O_EXCL,777)<0)
printf("cannot creat fifoserver\n");
memset(buf,0,100);
fd=open(FIFO,O_RDONLY|O_NONBLOCK);
if(fd<0)
{
perror("open\n");
exit(1);
}
else
{
memset(buf,0,100);
n=read(fd,buf,sizeof(buf));
printf("%s read from files\n",buf);
pause();
unlink(FIFO);
}
}
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "/software/hello"
int main(int argc char *argv[])
{
int fd,n;
char buf[100];
fd=open(fd,O_WRONLY|O_NONBLOCK,0);
if(fd<0)
{
perror("open\n");
exit(1);
}
esle
{
strncpy(buf,argv[1],100);
n=write(fd,buf,100);
printf("%d bytes write to files,%s ",n,buf);
exit(0);
}
return 0;
}
2 信号通信
信号(signal)机制是Unix系统中最为古老的进程间通信机制,很多条件可以产生一个信号:
当用户按某些按键时,产生信号。
硬件异常产生信号:除数为0、无效的存储访问等等。这些情况通常由硬件检测到,将其通知内核,然后内核产生适当的信号通知程,例如,内核对正访问一个无效存储区的进程产生一个SIGSEGV信号。
进程用kill函数将信号发送给另一个进
程。
用户可用kill命令将信号发送给其他进程
信号处理
1,忽略,其中两种信号不能忽略。1 SIGKILL 2SIGSTOP
2.按系统默认方式处理(大多数终止该进程)
3 按用户指定函数处理
发送信号的主要函数有kill和raise。
区别:
Kill既可以向自身发送信号,也可以向其他进程发送信号。与kill函数不同的是,raise函数是向进程自身发送信号。
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signo)
int raise(int signo)
kill的pid参数有四种不同的情况:
1、pid>0
将信号发送给进程ID为pid的进程。
2、pid == 0
将信号发送给同组的进程。
3、pid < 0
将信号发送给其进程组ID等于pid绝对值的进程。
4、pid ==-1
将信号发送给所有进程。
Alarm函数
#include <unistd.h>unsigned int alarm(unsigned int seconds)
seconds 表示seconds秒之后发送一个SIGALRM信号
signal函数
#include <signal.h>
void (*signal (int signo, void (*func)(int)))(int)
Func可能的值是:
1、SIG_IGN:忽略此信号
2、SIG_DFL: 按系统默认方式处理
3、信号处理函数名:使用该函数处理
例子
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
void my_func(int signo);
int main(void)
{
printf("waiting signal of SIGQIUT or SIGINT now\n");
signal(SIGQUIT,my_func);
signal(SIGINT,my_func);
pause();
return 0;
}
void my_func(int signo)
{
if(signo==SIGQUIT)
{
printf("i have get a signal of SIGQUIT\n");
}
if(signo==SIGINT)
{
printf("i have get a signal of SIGINT\n");
}
}
3 共享内存通信
共享内存是指多个内存共享一部分物理内存,一个进程向内存区写入数据,其他进程都能看到其中内容。
共享内存分两个步骤
一 创建共享内存
二 映射共享内存,就是将共享内存映射到所有进程中去。
创建共享内存的函数
int shmget(key_t key,int size,int shmflg )
key标识共享内存的键值: 0/IPC_PRIVATE。当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中又设置IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。返回值:如果成功,返回共享内存标识符;如果失败,返-1.
映射共享内存
int shmat(int shmid,char *shmaddr,int flag)
shmid :shmget函数返回的共享内存标识符。
shmaddr:映射地址,一般为0
flag :什么方式返回映射地址,一般为0.
若成功,shmat返回共享内存映射到进程的地址,不成功返回 -1.
当一个进程不需要共享内存时,需要从进程地址脱离。int shmdt(char *shmaddr)
例子
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PERM S_IRUSR|S_IWUSR
int main(int argc,char *argv[])
{
int shmid;
pid_t pid;
char *r_addr,*w_addr;
shmid=shmget(IPC_PRIVATE,1024,PERM);
pid=fork();
if(shmid==-1)
{
printf("creat share memroy failure\n");
exit(1);
}
else
{
if(pid>0)
{
w_addr=shmat(shmid,0,0);
strncpy(w_addr,argv[1],1024);
printf("%s write\n",argv[1]);
wait(NULL);
exit(0);
}
else if(pid==0)
{
sleep(2);
r_addr=shmat(shmid,0,0);
printf("%s read from files\n",r_addr);
exit(0);
}
}
4,消息队列
消息队列说白了就是一个ID号,跟进程ID号差不多意思。
分类;1POSIX消息队列 2 系统V消息队列
键值:每个消息队列对应一个唯一键值,类似每个进程对应一个PID。
获取键值:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char*pathname, char proj)
pathname 文件名
proj 项目名(不为0)
函数返回值为文件名的键值。
打开、创建一个消息队列函数
#include <sys/types.h>#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
key ftok返回键值
msgflg 标志位,IPC_CREAT IPC_EXCL(与CREAT联用) IPC_NOWAIT(读写消息队列要求无法满足时不阻塞)
返回值:与健值key相对应的消息队列描述字。
注意:若无键值对应的消息队列,而msgflg包含IPC_CREAT,而且key参数也为IPC_PRIVATE时,也创建一个消息队列。
发送消息函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg)
msqid :消息队列ID,即msgget()返回值。
msgp :存放消息的结构。
struct msgbuf
{
long mtype; /* 消息类型> 0 */
char mtext[1]; /* 消息数据的首地址*/
}
msgsz :发送消息大小
msgflg :消息标志位,有意义的msgflg标志为IPC_NOWAIT。
接收消息
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)
msqid :消息队列ID,即msgget()返回值。
msgtyp :从哪个消息类型中接受数据,必须跟发送消息的消息类型一致。
msgflg :消息标志位,有意义的msgflg标志为IPC_NOWAIT.
注意 : 成功接受到一条消息后队列中该消息将会被删除,跟管道通信一样。
例子
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
struct msg_buf
{
int mtype;
char data[255];
};
int main(char argc,char *argv[])
{
msg_buf buf;
key_t key;
int file_id;
key=ftok("aa",1);
file_id=msgget(key,IPC_CREAT |0777);
buf.mtype=1;
strcpy(buf.data,"test! test!");
msgsnd(file_id,&buf,1024,IPC_NOWAIT);
memset(&buf,'/0',sizeof(buf))
msgrcv(file_id,&buf,sizeof(buf.data),1,IPC-NOWAIT);
printf("%s receive from files",buf.data);
}
5 信号量
信号量是一个非负数,主要用于保护临界资源。进程可以根据它判断能否访问共享资源,还可用于进程同步。
分类
1 二值信号灯 只能取0或1,表示资源可用和不可用的状态。
2 计数信号灯:信号灯的值可以取任意非负值。
创建、打开一个信号灯函数
#include <sys/types.h>#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg)
key ftok返回值
semflg 标识,同消息队列
nsems:指定打开或者新创建的信号灯集中将包含信号灯的数目。
操作
int semop(int semid, struct sembuf *sops, unsigned nsops)
功能:对信号量进行控制。
semid:信号量集的ID
sops:是一个操作数组,表明要进行什么操作
struct sembuf {
unsigned short sem_num; /* semaphore
index in array */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
sem_num:要操作的信号量在信号量集中的编号,第一个信号的编号是0。
sem_op:如果其值为正数,该值会加到现有的信号量值中,通常用于释放信号量;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值,通常用于获取信号量;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。
Sem_flg:信号操作标志,可能的选择有两种:
IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
PC_UNDO:程序结束时(不论正常或不正常)释放信号量,这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
nsops:sops所指向的数组的元素个数。