目录
1.进程相关概念
1.什么是程序,什么是进程,有什么区别
程序是静态的概念,gcc xxx.c -o pro,磁盘中生成的pro文件叫程序
进程是程序的一次的活动,只要程序跑,系统中就多了一个进程
2.如何查看进程
ps 或ps -aux|grep 要查找的进程
grep用来过滤
例:ps -aux|grep init
使用top指令查看,类似windows任务管理器
3.什么是进程标识符
每个进程都有一个非负整数表示唯一ID,叫做pid,有点像身份证
pid=0:称为交换进程,作用:进程调度
pid=1:init进程,作用:系统初始化
调用getpid()获取自身进程标识符,getppid()获取获取父进程标识符
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
//pid_t getpid(void);
//pid_t getppid(void);
pid = getpid();
printf("my pid is %d\n",pid);
while(1);
return 0;
}
4.什么是父进程,什么是子进程
进程A创建了进程B,A就是父进程,B就是子进程
5.C程序的存储空间是如何分配?
·正文段。这是由CPU执行的机器指令部分。通常,正文段是可共享的,所以即使是频繁执行的程序(如文本编辑器、C编译器和shell等)在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外而修改其自身的指令。
·初始化数据段。通常将此段称为数据段,它包含了程序中需明确地赋初值的变量。例外,C程序中出现在任何函数之外的声明。
int maxcount = 99;
·非初始化数据段。通常将此段成为bss段,这一名称来自与一个早期的汇编运算符,意思是"block started by symble"(由符号开始的块),在程序开始执行之前,内核将此段中的数据初始化为0或空指针。出现在任何函数外的C声明
long sum[1000];
使此变量存放在非初始化数据段中。
·栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次调用函数时,其返回时地址以及调用者的环境信息(例如某些机器寄存器的值)都存放在栈中。然后,最近被调用C函数。递归函数每次调用自身时,就使用一个新的栈帧,因此一个函数调用实例中的变量集不会影响另一个函数调用实例中的变量。
·堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位与非初始化数据段和栈之间。
2.创建进程函数fork的使用
pid_t fork(void);
fork函数调用成功,返回两次。
返回值为0,代表当前进程是子进程,非负数为父进程
创建子进程的目的:复制父进程,在父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,让子进程去处理。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
fork();
printf("my pid is %d\n",pid);
return 0;
}
返回两次:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
fork();
printf("my pid is %d,current pro id:%d\n",pid,getpid());
return 0;
}
~
~
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
fork();
if(pid == getpid()){
printf("this is father print\n");
}else{
printf("this is child print,child pid = %d\n",getpid());
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("father: pid = %d\n",getpid());
pid = fork();
if(pid > 0){
printf("this is father print, pid = %d\n",getpid());
}else if(pid == 0){
printf("this is child print,child pid = %d\n",getpid());
}
return 0;
}
应用场景(模拟网络等待):
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int data;
while(1){
printf("please input a data\n");
scanf("%d",&data);
if(data == 1){
pid = fork();
if(pid > 0){
}else if(pid == 0){
while(1){
printf("do net request,pid=%d\n",getpid());
sleep(3);
}
}
}else{
printf("wait, do nothing\n");
}
}
return 0;
}
~
3.vfork创建进程
vfork与fork的区别:
1.vfork直接使用父进程的存储空间,不拷贝
2.vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int cnt=0;
pid_t pid;
pid_t repid;
pid=getpid();
repid=vfork();
if(repid > 0){
while(1){
printf("cnt=%d\n",cnt);
printf("this is father pid,repid=%d\n",repid);
sleep(1);
}
}
else if(repid == 0){
while(1){
printf("this is child pid,repid=%d\n",repid);
sleep(1);
cnt++;
if(cnt==2){
exit(0);
}
}
}
return 0;
}
4.进程退出
正常退出
1.main函数调用return
2.进程调用exit(),标准C库
3.进程调用_exit()或者_Exit(),系统调用
4.进程最后一个线程返回
5.最后一个线程调用pthread_exit
异常退出
1.调用abort
2.当进程收到某些信号时,如ctrl+c
3.最后一个线程对取消(cancellation)请求作出响应
注意:不管进程如何终止,最后都会执行内核中的同一段代码,这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。
5.父进程等待子进程退出
pid_t wait(int *status);
status参数:整型指针
非空:子进程退出状态放在它所指向的地址中
空:不关心退出状态
int main(){
pid_t pid;
pid_t repid;
int cnt=0;
int status;
pid=getpid();
repid=fork();
if(repid > 0){
wait(&status);//等待子进程退出
//子进程正常退出,WEXITSTATUS
printf("status=%d\n",WEXITSTATUS(status));//把终止的子进程通知给父进程
while(1){
printf("this is father pid,repid=%d\n",repid);
sleep(1);
}
}
else if(repid == 0){
while(1){
printf("this is child pid,repid=%d\n",repid);
sleep(1);
cnt++;
if(cnt==5){
printf("cnt=%d\n",cnt);
exit(5);
}
}
}
return 0;
}
6.exec族函数
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
//int execl(const char *path, const char *arg, .../* (char *) NULL */);
/* path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
*/
printf("before execl\n");
if(execl("/bin/ls","ls",NULL,NULL) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
int main(){
//int execlp(const char *file, const char *arg, .../* (char *) NULL */);
printf("before execl\n");
if(execlp("ls","ls","-l",NULL) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
int main(){
//int execv(const char *path, char *const argv[]);
char* argv[]={"ls",NULL,NULL};
if(execv("/bin/ls",argv) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
int main(){
// int execvp(const char *file, char *const argv[]);
char* argv[]={"ls","-l",NULL};
if(execvp("ls",argv) == -1)
{
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
7.exec配合fork使用
int main(){
pid_t pid;
pid_t repid;
int data;
while(1){
printf("please input a number:");
scanf("%d",&data);
if(data==1){
repid=fork();//创建进程
if(repid > 0){
wait(NULL);
}
if(repid == 0){
execl("./changedata.out","changeNumToFile.c","changeNum.c",NULL);
exit(0);
}
}
else{
printf("waiting!\n");
}
}
return 0;
}
//changeNumToFile.c
int main(int argc,char* argv[]){
int fd;
char* readBuf;
//int open(const char *pathname, int flags);
fd=open("./changeNum.c",O_RDWR);//打开文件
int size=lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
//ssize_t read(int fd, void *buf, size_t count);
readBuf=(char*)malloc(sizeof(char)*size+8);
int n_read=read(fd,readBuf,size);
char* p=strstr(readBuf,"LENG=");
if(p==NULL){
printf("not fingd\n");
}
else{
p=p+strlen("LENG=");
*p='5';
lseek(fd,0,SEEK_SET);
int n_write=write(fd,readBuf,size);
}
close(fd);
return 0;
}
8.system函数
#include <stdlib.h>
int system(const char *command);
system()函数返回值如下:
成功,则返回进程的状态值
当sh不执行时,返回127;
失败返回 -1
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(){
pid_t pid;
pid_t repid;
int data;
while(1){
printf("please input a number:");
scanf("%d",&data);
if(data==1){
repid=fork();//创建进程
if(repid > 0){
wait(NULL);
}
if(repid == 0){
//execl("./changeNumToFile.out","changeNumToFile.c","changeNum.c",NULL);
system("./changeNumToFile.out");//执行完该函数后,还会执行后面的函数
exit(0);
}
}
else{
printf("waiting!\n");
}
}
return 0;
9.popen函数
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
比system在应用中的好处:可以获取运行的输出结果
#include <stdio.h>
#include <stdlib.h>
// FILE *popen(const char *command, const char *type);
// FILE *fopen(const char *path, const char *mode);
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
int main(){
// system("ls");
FILE* fp;
char ret[1024];
fp=popen("ls -l","r");
int n_read=fread(ret,1,1024,fp);
if(n_read != -1){
printf("read sucess!\n");
printf("return n_read=%d,read data=%s\n",nread,ret);
}
else{
printf("no read datas\n");
}
return 0;
}