进程:一个程序的一次执行过程,是系统调度的单位,资源管理和程序执行的最小单位,线程是调度的最小单位。
程序:是静态的,是一个可执行文件,保存在硬盘上的一些指令的有序集合。
进程的执行过程:指令和数据是存放在硬盘上的,这是前提。首先运行程序---》系统会将硬盘上的指令和数据加载到内存中,(如果cpu每次都去访问硬盘,速度是非常慢的,而直接访问内存,这将非常的快)---》cpu则访问内存执行代码;
进程包含三个段:代码段(存放程序代码)。
数据段:存放全局变量,malloc动态分配的地址空间,以及常数。
堆栈段:存放局部变量,子程序返回地址,子程序参数。
内存分为以下5个部分:(一个进程以创建,系统就会自动为该进程分配4G的内存,3G用户空间,1G内核空间(通过系统调用来访问内核空间))
栈区:由编译器自动分配和释放,存放函数的参数,局部变量等。
堆区:由程序员分配释放,若没有释放,程序结束时有OS来释放。
全局区(static):存放全局变量和静态变量,初始化了的放在一边,未初始化的放一边。
程序代码区:存放程序的二进制代码。
文字常量区:常量字符串,程序结束后由OS释放。
每个进程都有一个独有的pid
获取pid :函数有:getpid(); getppid();
命令有:ps –ef ps -aux
Linux进程的7个状态:1.执行态,2.就绪态,3.阻塞态(浅睡眠,深睡眠),4.暂停态,5.死亡态(僵尸态(struct task_strucct未被清除),死亡态)。
进程的操作api:fork, wait, waitpid, signal, pause, exec, kill ,exit
进程创建:最后由父进程回收子进程结构体。
#include "myhead.h"//创建多个子进程,他们属于同一父进程。
int main(int argc, char **argv)
{
int n;
n = atoi(argv[1]);
pid_t a;
int i;
for(i=0; i<n; i++){
a = fork();
if(a > 0) // parent//父进程中返回所创建的子进程的pid
continue;
if(a == 0) // child可以通过getpid()获取该子进程的pid
break;
}
if(a > 0){
for(i=0; i<n; i++)
wait(NULL);
}
printf("pid: %d, ppid: %d ",
getpid(), getppid());
exit(0);
}//
Exec():fork创建的子进程几乎拷贝了父进程的全部的内容。Exec用于更新子进程中的代码,数据段,栈段,使该进程只含有子进程它自身的代码。
代码示例:
exec函数
execl , execv, execle, execvp, execve.
int main()
{
fork()
if( ==0)
{
execl("可执行文件路径", 可执行文件名,"111","222","333",NULL);//需在子进程中执行。更多示例请参考代码文件夹。
}
}
Linuc守护进程:8个步骤
#include <unistd.h>
#include <sys/stat.h>
#include <syslog.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/file.h>
#include <stdio.h>
#include <sys/resource.h>
#include <signal.h>
int main(void)
{
pid_t pid;
int max_fd, i;
/***************************************
1. generate a child process, to ensure
successfully calling setsid()
****************************************/
pid = fork();
if(pid > 0)
exit(0);
/*********************************************
2. ignore the signal SIGHUP, prevent the
process from being killed by the shutdown
of the present controlling termination
**********************************************/
signal(SIGHUP, SIG_IGN);
/******************************************************
3. call setsid(), let the first child process running
in a new session without a controlling termination
*******************************************************/
setsid();
/*************************************************
4. generate the second child process, to ensure
that the daemon cannot open a terminal file
to become its controlling termination
**************************************************/
pid = fork();
if(pid > 0)
exit(0);
/*********************************************************
5. detach the daemon from its original process group, to
prevent any signal sent to it from being delivered
**********************************************************/
setpgrp();
/*************************************************
6. close any file descriptor to release resource
**************************************************/
max_fd = sysconf(_SC_OPEN_MAX);
for(i=0; i<max_fd; i++)
close(i);
/******************************************
7. clear the file permission mask to zero
*******************************************/
umask(0);
/****************************************
8. change the process's work directory,
to ensure it won't be uninstalled
*****************************************/
chdir("/");
// Congratulations! Now, this process is a DAEMON!
pause();
return 0;
}
进程间通信:为何要有进程通信:两个进程同时进行时,这两个进程间需要有些交互。比如MP3播放器,解码是一个进程,同时另一个用户操作也是一个进程,解码之后就需要将解码的数据交给用户操作的进程来执行。
无名管道:
注意一下几点: 只有能用于具有亲缘关系的进程之间的通信。
半双工通信方式,具有固定的读端和写端fd[0]读,fd[1]写。
它是一种特殊的文件,使用read,write来读写。
基本操作:
创建第三方(无名管道):pipe(fd);
将第三方与进程形成映射:fd
读写数据read,write;
撤销映射:close(fd[0]);
删除第三方;
int main()
{
int fd[2]
pipe(fd);//创建管道,必须在进程之前,不然父进程就无法,将其拷贝给子进程。
r=fork();//创建进程
if(r==0)//子进程从fd中读出数据
{
close(fd[1]);//使用fd[0]时,记得关闭子进程的fd[1].
read(fd[0],buf,4);
printf();
close(fd[0]);
}
else if(r>0)//父进程向fd[1]中写入数据
{
sleep(1);
close(fd[0]);
write(fd[1],"abef",);
close(fd[1]);
}
}
关于读写需注意几点:1.当管道中无数据时,读操作会阻塞
2.向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。
3.如果读端已关闭 ,写数据 ,进程退出
4.如果写端已关闭,读数据 --->0
有名管道: 可以使互不相关的两个进程间进行通信。
遵循先进先出的规则。
通过文件io来操作。
不支持如lseek();
- 创建有名管道mkfifo
- 映射有名管道open(fd)
- 读写:read,write
- 撤销映射:close(fd);
- 删除有名管道文件;
int main()
{
mkfifo("./fifo",O_CREAT|0666)//创建一个有名管道,是一个管道文件p。
int fd=open("./fifo",O_WRONLY) //打开管道得到一个文件描述符fd
wrtie(fd,“” ,)//向文件描述符中写入数据
close(fd); // 关闭文件描述符
}
p2.c
int main()
{
int fd=open("./fifo",O_RDONLY);//打开同一个管道文件,得到一个文件描述符
char buf[12];//
int r=read(fd,buf,11);//读取文件描述符中的数据
buf[r]=' ';对字符串的操作,这不很关键,没有这步,易出现乱码。
printf("%s",buf);
close(fd);
}
关于读写需要注意的几点容易出错,或者打不开管道:
① open(const char *path, O_RDONLY );
在这种情况下,open 调用将阻塞,除非有一个 进程以写方式打开同一个FIFO,否则它不会返回。
②open(const char *path, O_RDONLY|O_NONBLOCK )
即使没有其它进程以写方式打开FIFO,这open调 用也将成功并马上返回
③open(const char *path, O_WRONLY)
open调用将阻塞,直到有一个进程以读方式打开同一个 FIFO为止。
④open(const char *path, O_WRONLY|O_NONBLOCK)
open调用总是立刻返回,便如果没有进程以读方式打开FIFO文件, open调用将返回一个错误(-1)并且FIFO也不会被打开。
比较好:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<string.h>
int main()
{
if(mkfifo("/home/gec/fifo",O_CREAT|0666)==-1)
perror("mkfifo");
char str[10];
int fd=open("/home/gec/fifo",O_WRONLY);
printf("start ");
if(fd==-1)
{
printf("open error: ");
}
while(1)
{
pid_t ret= fork();
if(ret<0)
{
printf("fork1 error:");
}
if(ret==0)
{
sprintf(str,"%d",getpid());//spriintf的使用;
write(fd,str,strlen(str));//strlen()的使用
printf("%s ",str);
sleep(1);
printf("write succes ");
exit(0);
}
wait(NULL);
}
}
信号:
在软件层次上对中断机制的一种模拟,是一种异步通信模式。
可以直接进行内核进程和用户进程之间的交互。
信号发送,信号阻塞(OS),信号的响应。
Raise(int sig)//向自己发送信号
Alarm(5)//5s后向自己发送SIGALRM信号,接收到这个信号后,进程遇到pause(),暂停5秒后继续向下执行。(一个进程最多有一个闹钟,如果有多个闹钟,以后一个为准)
//#include "myhead.h"
#include <unistd.h>
#include<signal.h>
#include<sys/types.h>
#include <sys/wait.h>
#include<stdio.h>
#include <stdlib.h>
pid_t a, b;//灵活使用全局变量
void catch_sig(int sig)
{
kill(a, SIGUSR1);/向子进程发送信号SIGUSR1
kill(b, SIGUSR2);
printf("%d,%d ",a,b);
}
void f(int sig)
{
printf("child 1 catch the signal: %d ", sig);
}
void g(int sig)
{
printf("child 2 catch the signal: %d ", sig);
}
int main(void)
{
signal(SIGINT, catch_sig);//父进程中注册信号SIGINT
a = fork();
if(a > 0){
b = fork();
if(b > 0){ // parent
pause();
wait(NULL);//等待子进程退出,父进程继续执行
wait(NULL);
printf("parent exit! ");
exit(0);
}
else if(b == 0){ // child 2
printf("pid=%d ",getpid());
signal(SIGUSR2, g);//注册信号SIGUSR2
pause();
exit(0);
}
}
else if(a == 0){ // child 1
printf("pid=%d ",getpid());
signal(SIGUSR1, f);
pause();//暂停进程信号
exit(0);
}
}
Alarm:
#include <unistd.h>
#include<signal.h>
#include<sys/types.h>
#include <sys/wait.h>
#include<stdio.h>
#include <stdlib.h>
int main()
{
int ret;
ret =alarm(5);
printf("wakenff ");
sleep(1);
printf("after sleep1, %d ",alarm(3));
pause();
printf("waken ");
return 0;
}
Forkkill:
#include <unistd.h>
#include<signal.h>
#include<sys/types.h>
#include <sys/wait.h>
#include<stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int ret;
if((pid=fork())<0)
{
perror("fork");
exit(1);
}
if(pid==0)
{
printf("chiled process ");
raise(SIGSTOP);
printf("chiled process exit ");
exit(0);
}
else
{ sleep(1);
printf("pid =%d ",pid);
if((waitpid(pid,NULL,WNOHANG))==0)
{
kill(pid,SIGKILL);
printf("kill %d ",pid);
}
}
}
Signaltest:
#include <unistd.h>
#include<signal.h>
#include<sys/types.h>
#include <sys/wait.h>
#include<stdio.h>
#include <stdlib.h>
void my_func(int sign)
{
if(sign==SIGINT)
printf("i have got sigint ");
else if(sign==SIGQUIT)
printf("i have got sigquit ");
}
int main()
{
printf("waitting for signal sigint or sigquit ");
signal(SIGINT,my_func);
signal(SIGQUIT,my_func);
pause();
printf("exit ");
exit(0);
}
拓展:
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
int output(sigset_t set)
{
printf("set.val[0]=%x ",set.__val[0]);
}
void handler(int sig)
{
int i;
sigset_t sysset;
printf(" nin hadler sig=%d ",sig);
sigprocmask(SIG_SETMASK,NULL,&sysset);
output(sysset);
printf("return ");
}
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
当sa_flags=SA_SIGINFO,时,使用第二个函数原型。
int main()
{
struct sigaction act;
sigset_t set,sysset,newset;
sigemptyset(&set);
sigemptyset(&newset);//清空结构体阻塞信号集
sigaddset(&set,SIGUSR1);//将信号加入到结构体阻塞信号集sysset中
sigaddset(&newset,SIGUSR2);
printf(" add sigusr1,the value of set");
output(set);//200
printf(" add sigusr2,the value of newset");
output(newset);//800
printf(" after set proc block set ,and then read to sysset ");
//将结构体阻塞信号集(在命令程序运行期间阻塞)set加入到进程阻塞信号集中
sigprocmask(SIG_SETMASK,&set,NULL);
//将进程阻塞信号集加入到结构体阻塞信号集sysset中
sigprocmask(SIG_SETMASK,NULL,&sysset);
printf("system mask is: ");
output(sysset);//2a00>200+800
printf("install sigalrm,and the act.sammask is newset(siguser2) ");
act.sa_handler=handler;
act.sa_flags=0;//表示使用结构体中的前一个函数原型
act.sa_mask=newset;
sigaction(SIGALRM,&act,NULL);//运行之后该信号会自动加入到进程阻塞信号集(始终都阻塞)中。
pause();
printf("after exec isr ");
sigemptyset(&sysset);
sigprocmask(SIG_SETMASK,NULL,&sysset);
output(sysset);//200
}
信号量集:systemV:(semphore)
- ftok:得到一个唯一的semid;
- 创建一个信号量集semget,返回一个唯一的semid;
- 初始化信号量集semctl(SETVAL)
- 判断信号量的值semctl(GETVAL);
- 操作信号量的值semop(),主要是设置一个结构体struct sembuf
{
Short sem_num;信号量编号
Short sem_op;对应的操作+1(v),-1(p)
Short sem_flg;SEM_UNDO
}
6.读写数据
7.删除信号量集semctl(IPC_RMID);
Share memory(共享内存):
最为高效的通信方式,进程可以直接读写数据,而不需要拷贝。
内核专门留出了一块内存,可以将其映射到用户空间某段内存。用户可以直接操作这段内存来间接操作内核内存,实现通信。
多个进程共享这段内存,需要同步机制(信号量)和互斥所.
共享内存使用步骤:
- ftok:得到一个唯一的key值
- 创建打开共享内存shmget,返回一个shmid;
- 映射一段共享内存shmat,返回内存地址的指针void *
- 读写操作
- 撤销共享内存映射shmdt
- 删除共享内存(一般有接收进程来实现)shmctl()
将一个结构体的数据写入是相同的操作:将两个进程的的共享内存的指针类型声明为该结构体类型即可,然后通过这个指针来获取数据,如果有多个结构体,指针需向后移。
SystemV.c(读取数据)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<sys/ipc.h>
int main()
{
key_t key = ftok("./",100);
if(key==-1)
perror("shmftok() error!/n");
int shmid = shmget(key,1024,IPC_CREAT|0666);
if(shmid==-1)
perror("shmget() error!/n");
char *p = shmat(shmid,NULL,0);
key_t semkey = ftok("./",102);
if(semkey ==-1)
perror("semftok() error! ");
int semid = semget(semkey,2,IPC_CREAT|0666);
if(semid == -1)
perror("semget() error! ");
// semctl(semid,0,SETVAL,1);
// if(semctl(semid,1,SETVAL,0)==-1)
// perror("semctl() error! ");
struct sembuf sembv;
sembv.sem_num = 0;
sembv.sem_op = 1;
sembv.sem_flg = SEM_UNDO;
struct sembuf sembp;
sembp.sem_num = 1;
sembp.sem_op = -1;
sembp.sem_flg = SEM_UNDO;
while(1)
{
if(semop(semid,&sembp,1)==-1)
// printf("%d ",semctl(semid,0,GETVAL));
perror("semop() error! ");
if(semctl(semid,0,GETVAL)==0)
{
printf("%s ",p);
semop(semid,&sembv,1);
}
else
{
perror("read error ");
}
}
}
Systemv1.c(写入数据)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<sys/ipc.h>
int main()
{
key_t key = ftok("./",100);
if(key==-1)
perror("shmftok() error!/n");
int shmid = shmget(key,1024,IPC_CREAT|0666);
if(shmid==-1)
perror("shmget() error!/n");
char *p = shmat(shmid,NULL,0);
key_t semkey = ftok("./",102);
if(semkey ==-1)
perror("semftok() error! ");
int semid = semget(semkey,2,IPC_CREAT|0666);//创建信号量集,返回semid
if(semid == -1)
perror("semget() error! ");
semctl(semid,0,SETVAL,1);
if(semctl(semid,1,SETVAL,0)==-1)
perror("semctl() error! ");
struct sembuf sembv;
sembv.sem_num = 1;
sembv.sem_op = 1;
sembv.sem_flg = SEM_UNDO;
struct sembuf sembp;
sembp.sem_num = 0;
sembp.sem_op = -1;
sembp.sem_flg = SEM_UNDO;
while(1)
{
// printf("%d ",semctl(semid,0,GETVAL));
if(semop(semid,&sembp,1)==-1)
//printf("%d ",semctl(semid,0,GETVAL));
perror("semop() error! ");
if(semctl(semid,0,GETVAL)==0)
{
scanf("%s",p);
semop(semid,&sembv,1);
}
else
{
perror("write error ");
}
}
}
消息队列:
- 通过ftok得到一个唯一的key值
- 创建消息队列msgget(),返回一个唯一的msgid;
- 发送消息,接收消息;消息结构体典型的类型:struct msgbuf
- {
- long mtype;
- char mtext[];
- }
- 删除消息队列:msgctl(IPC_RMID)
message.c(先发后收)两个进程之间进行通信
#include<stdlib.h>
#include<stdio.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<string.h>
#include<sys/sem.h>
struct msgbuf
{
long mtype;
char mtext[12];
};
int main()
{
key_t key = ftok("./",20);
if(key==-1)
perror("ftok() error ");
int msgid = msgget(key,IPC_CREAT|0666);
if(msgid==-1)
perror("msgget() error! ");
struct msgbuf send1,rcv1;
send1.mtype = 200;
strcpy(send1.mtext,"hello!");
key_t semkey = ftok("./",21);
int semid = semget(semkey,4,IPC_CREAT|0666);
if(semid ==-1)
perror("semget() error! ");
semctl(semid,0,SETVAL,1);
semctl(semid,1,SETVAL,0);
semctl(semid,2,SETVAL,0);
semctl(semid,3,SETVAL,0);
struct sembuf semb0,semb1,semb2,semb3;
semb0.sem_num = 0;
semb0.sem_op = -1;
semb0.sem_flg = SEM_UNDO;
semb1.sem_num = 1;
// semp1.sem_op = 1;
semb1.sem_flg = SEM_UNDO;
semb2.sem_num = 2;
// semp.sem_op = -1;
semb2.sem_flg = SEM_UNDO;
semb3.sem_num = 3;
// semp.sem_op = -1;
semb3.sem_flg = SEM_UNDO;
while(1)
{
semb0.sem_op = -1;
if(semop(semid,&semb0,1)==-1)
perror("semop() error! ");
if(semctl(semid,0,GETVAL)==0)
{
char messag[30];
printf("please input:");
scanf("%s",messag);
strcpy(send1.mtext,messag);
msgsnd(msgid,&send1,strlen(send1.mtext)+1,0);
semb1.sem_op = 1;
semop(semid,&semb1,1);
}
semb3.sem_op = -1;
semop(semid,&semb3,1);
if(semctl(semid,3,GETVAL)==0)
{
msgrcv(msgid,&rcv1,sizeof(rcv1),100,0);
printf("accept:");
printf("%s ",rcv1.mtext);
semb0.sem_op = 1;
semop(semid,&semb0,1);
}
// else
// {
// perror("msg1 error! ");
// }
}
}
message2.c(先收后发)两个进程之间进行通信
#include<stdlib.h>
#include<stdio.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<string.h>
#include<sys/sem.h>
struct msgbuf
{
long mtype;
char mtext[12];
};
int main()
{
key_t key = ftok("./",20);
if(key==-1)
perror("ftok() error ");
int msgid = msgget(key,IPC_CREAT|0666);
if(msgid==-1)
perror("msgget() error! ");
struct msgbuf send1,rcv1;
send1.mtype = 100;
// strcpy(send1.mtext,"hello!");
key_t semkey = ftok("./",21);
int semid = semget(semkey,4,IPC_CREAT|0666);
if(semid ==-1)
perror("semget() error! ");
// semctl(semid,0,SETVAL,1);
// semctl(semid,1,SETVAL,0);
struct sembuf semb0,semb1,semb2,semb3;
semb0.sem_num = 0;
// semb0.sem_op = -1;
semb0.sem_flg = SEM_UNDO;
semb1.sem_num = 1;
// semb1.sem_op = 1;
semb1.sem_flg = SEM_UNDO;
semb2.sem_num = 2;
// semb1.sem_op = 1;
semb2.sem_flg = SEM_UNDO;
semb3.sem_num = 3;
// semb1.sem_op = 1;
semb3.sem_flg = SEM_UNDO;
while(1)
{
semb1.sem_op=-1;
// semop(semid,&semb1,1);
if(semop(semid,&semb1,1)==-1)
perror("semop() error! ");
if(semctl(semid,1,GETVAL)==0)
{
msgrcv(msgid,&rcv1,sizeof(rcv1),200,0);
printf("accept:");
printf("%s ",rcv1.mtext);
semb2.sem_op = 1;
semop(semid,&semb2,1);
}
semb2.sem_op = -1;
semop(semid,&semb2,1);
if(semctl(semid,2,GETVAL)==0)
{
char messag[30];
printf("please input:");
scanf("%s",messag);
strcpy(send1.mtext,messag);
msgsnd(msgid,&send1,strlen(send1.mtext)+1,0);
semb3.sem_op = 1;
semop(semid,&semb3,1);
}
// else
// {
// perror("msg2 error! ");
// }
}
}