1.进程相关概念介绍
1.查看电脑上运行的进程:ps -a
2.进程创建函数fork()
NAME
fork - create a child process
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
RETURN VALUE(返回值) 在父进程里,返回的是子进程的id, 在子进程里返回:0
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure,
-1 is returned in the parent, no child process is created,and errno is set appropriately.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
int main(int argc, char *argv[])
{
pid_t pid =-1;
int num = 0;
char camsg[64] = {'\0'};
pid = fork();
if(-1 == pid)
{
perror("fork");
return -1;
}
else if(pid >0)
{
//getpid():获得本进程的进程号;
printf("parent: self_pid = %d,child_pid = %d\n",getpid(),pid);
num = 3;
strcpy(camsg,"this is parent process!\n");
}
else if(0 == pid)
{
//子进程获得父进程pid :getppid()
printf("child: parent_pid = %d,self_pid = %d\n",getppid(),getpid());
num = 6;
strcpy(camsg,"this is child process!\n");
}
while(num)
{
printf("%s",camsg);
num--;
sleep(1);
}
return 0;
}
问题环节:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
int main(int argc, char *argv[])
{
fork();
fork();
//请问现在有多少个进程?
return 0;
}
思考题:
会有多少个进程呢?
3.僵尸进程:
4.如何处理僵尸进程
1, wait函数:
NAME
wait, waitpid, waitid - wait for process to change state
SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
参数wstatus:进程状态
返回值: 成功返回进程号,失败返回-1
RETURN VALUE
wait(): on success, returns the process ID of the terminated child; on
error, -1 is returned.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid = -1;
pid = fork();
if(pid>0)
{
pid_t temp = -1;
int status = 0;
temp = wait(&status);//调用wait函数阻塞等待(对子进程形成的僵尸进程处理,并获得子进程的结束状态)
//这个用法不好
//若没有子进程结束,则父进程一直阻塞等待子进程结束
//若有一个子进程结束,则父进程马上对其进行回收,回收之后不再等待,继续往下运行
printf("wait pid = %d\n",temp);
printf("child ret = %d\n",WEXITSTATUS(status));WEXITSTATUS 是wait里的宏,可以man以下
if(WIFEXITED(status))
{
printf("子进程正常结束\n");
}
else
{
printf("子进程非正常结束\n");
}
while(1)
{
printf("parent:self_id = %d,child_pid = %d\n",getpid(),pid);
sleep(1);
}
}
else if(0 == pid)
{
//子进程获得父进程pid :getppid()
int num = 6;
//strcpy(camsg,"this is child process!\n");
while(num)
{
printf("child: parent_pid = %d,self_pid = %d\n",getppid(),getpid());
num--;
sleep(1);
}
}
return 0;
}
运行结果:
2.使其成为孤儿进程,由祖宗进程来接管,编程如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid = -1;
pid = fork();
if(pid>0)//A
{
wait(NULL);
while(1)
{
printf("parent:self_id = %d,child_pid = %d\n",getpid(),pid);
sleep(1);
}
}
else if(0 == pid)
{
pid_t pid2 = fork();//b
if(0 == pid2)//c
{
//子进程获得父进程pid :getppid()
int num = 6;
while(num)
{
printf("child: parent_pid = %d,self_pid = %d\n",getppid(),getpid());
num--;
sleep(1);
}
return 88;
}
}
return 0;
}
由上我们可以看出父子进程都是同时运行的,;
父进程不用等待子进程结束, 比wait()函数好点,能更好的去处理僵尸进程
3.通过对子进程结束时自动发出的信号处理方式重定义来处理僵尸进程
5.exec函数族:
通过已经写好的程序来启动一个进程,不过与fork()函数不同,不是拷贝父进程的所有资源
会将当前的进程镜像替换掉,使用新的进程镜像,但是依旧保留并使用原来的进程号(鸠占鹊巢)
NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file
SYNOPSIS
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg,...)
int execlp(const char *file, const char *arg,...)
int execle(const char *path, const char *arg, (char *) NULL, char * const envp[] );
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
int execl(const char *path, const char *arg,(char *) NULL ); path:要执行文件的路径 arg:文件执行方式
RETURN VALUE :(只有在创建进程失败时才会返回,返回-1,成功时原来的进程都被替换了,所以没有返回
The exec() functions return only if an error has occurred. The return
value is -1, and errno is set to indicate the error.
#include <stdio.h>
#include<string.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
printf("begin main\n");
//1.到给定的路径下面去找要执行的命令
//2.若命令找到了,则将后续的执行方式拼接起来形成一个可执行的格式,拼接的时候遇到
//NULL则停止拼接
int ret = execl("/usr/bin/gedit","gedit","readir.c"NULL);
if(-1 == ret)
{
perror("ret:");
}
printf("end\n");
return 0;
}
6. 进程间通信
1,进程通信之无名管道:
父子进程是如何通过管道进行通信的?实现代码:
NAME
pipe, pipe2 - create pipe
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
RETURN VALUE(成功返回0,失败返回-1)
On success, zero is returned. On error, -1 is returned, and errno is
set appropriately.
#include <stdio.h>
#include<string.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
int pipefd[2] = {0};
int ret = -1;
ret = pipe(pipefd);
if (-1 == ret)
{
perror("pipe");
return -1;
}
pid_t pid = fork();
if(pid>0)
{//parent
char camsg[64] = {'\0'};
while(1)
{
memset(camsg,'\0',sizeof(camsg));
printf("parent,please input msg:\n");
scanf("%s",camsg);
write(pipefd[1],camsg,strlen(camsg));
}
}
else if(0 == pid)
{//child
char camsg[64] = {'\0'};
while(1)
{
memset(camsg,'\0',sizeof(camsg));
read(pipefd[0],camsg,sizeof(camsg));
printf("child receive message:%s\n",camsg);
}
}
else if(-1 == pid)
{
perror("fork");
return -1;
}
return 0;
}
2.进程通信之有名管道:
通过管道文件来进行通信:
NAME
mkfifo, mkfifoat - make a FIFO special file (a named pipe)(一个特殊的文件)
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
创建管道文件:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
int ret = mkfifo(argv[1],0664);
if(-1 == ret)
{
perror("mkfifo");
return -1;
}
printf("make fifo %s success\n",argv[1]);
return 0;
}
运行结果:p:代表的是管道文件
读:
//管道读:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
int fd = open(argv[1],O_RDONLY);
char camsg[64] = {'\0'};
while(1)
{
memset(camsg,'\0',sizeof(camsg));
read(fd,camsg,sizeof(camsg));
if(0 == strcmp("exit",camsg))
{
break;
}
printf("recv msg:%s\n",camsg);
}
return 0;
}
//管道写
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
int fd = open(argv[1],O_WRONLY);
if(-1 == fd)
{
perror("open");
return -1;
}
char camsg[64] = {'\0'};
while(1)
{
printf("please input msg:\n");
memset(camsg,'\0',sizeof(camsg));
scanf("%s",camsg);
write(fd,camsg,strlen(camsg));
if(0 == strcmp("exit",camsg))
{
break;
}
}
return 0;
}
运行结果:
3.进程通信之共享内存
参数:key:对所申请的空间起的一个名字,用整数来标识这段空间
size:这块空间的大小
shmflag:权限
返回值:成功:返回共享内存的标识,失败:返回-1
查看共享内存的命令:ipcs -m;//电脑重启后,共享内存会被回收
4.内存映射
将文件或设备映射到内存里去
NAME
mmap, munmap - map or unmap files or devices into memory
SYNOPSIS
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
See NOTES for information on feature test macro requirements.
addr:要映射的内存地址
length:映射内存空间的大小
port:指定映射空间是用来干嘛的
The prot argument describes the desired memory protection of the map‐
ping (and must not conflict with the open mode of the file). It is
either PROT_NONE or the bitwise OR of one or more of the following
flags:
PROT_EXEC Pages may be executed.(执行)
PROT_READ Pages may be read.(读,获得文件的数据)
PROT_WRITE Pages may be written.(写)
PROT_NONE Pages may not be accessed.(什么也不做)
flags:
MAP_SHARED:自动更新到文件里去,其他进程就可以通过文件来获得修改的数据
MAP_PRIVATE:只供自己使用,同过将文件里的内容映射到内存里,供自己使用,
内存的里东西被修改时不同更新到文件里,其他进程不能共享
fd:映射文件的描述符
offset:从文件的哪个位置开始映射
返回值:如果成功的话,返回一个指针 ;失败返回一个宏:MAP_FILED(这个宏是void*(-1))