linux系统中父子进程间采用,LINUX编程学习笔记(十四) 创建进程与 父子进程内存空间…

  • Post author:
  • Post category:linux


原地址:http://blog.csdn.net/a8887396/article/details/9127701

1什么是进程:进程是一个执行中的程序

执行的程序: 代码->资源->CPU

进程有很多数据维护:进程状态/进程属性

所有进程属性采用的一个树形结构体维护

ps  -a//所有进程

ps -aue //有效进程

进程状态:(man ps)

D    Uninterruptible sleep (usually IO)

R    Running or runnable (on run queue)

S    Interruptible sleep (waiting for an event to complete)

T    Stopped, either by a job control signal or because it is being traced.

W    paging (not valid since the 2.6.xx kernel)

X    dead (should never be seen)

Z    Defunct (“zombie”) process, terminated but not reaped by its parent.

For BSD formats and when the stat keyword is used, additional characters may be

displayed:

<    high-priority (not nice to other users)

N    low-priority (nice to other users)

L    has pages locked into memory (for real-time and custom IO)

s    is a session leader

l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) 多进程

+    is in the foreground process group 前台进程

top

实时查看进程

pstree 查看进程树

kill 向进程发送信号

kill -s 信号 进程ID

kill -l  //查看所有信号

kill -s 9 224  //关闭224进程

2 创建进程

1  进程有关的创建函数

1.1 system

int system(const char*filename);

建立一个独立的进程,拥有独立的代码空间

等待新的进程执行完毕,system才返回(阻塞)

使用system调用函数(观察进程ID 观察阻塞)

新进程的返回值和system返回值有关系

1任何进程的返回值不要超过255

2 system中进程的返回值存放在system返回值的8-15位

可以通过printf(“%d\n”,r>>8 ); 这样来获得返回值

3 linux中有专用的获得返回状态的宏

WEXITSTATUS(status) //#include  #include

sys.c

#include

#include

intmain()

{

printf(“%d\n”,getpid());

sleep(2);

return254;

}

sys2.c

#include

#include

#include

#include

intmain()

{

intr;

printf(“%d\n”,getpid());

r = system(“./sleep”);

//printf(“%d\n”,r>>8 );

printf(“%d\n”,WEXITSTATUS(r));

}

gcc sys.c -o sleep

gcc sys2.c

./a.out

1.2 popen:创建子进程

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

案例:使用popen调用ls -l 并且建立一个管道读取输出

#include

#include

intmain()

{

FILE*fp = popen(“cat file”,”r”);//注意这里是读取输出,不是打开文件 不能直接写文件名

//FILE *fp = popen(“ls -l”,”r”);

if(!fp)

{

perror(“popen”);

return1;

}

intfd = fileno(fp);

intr;

charbuf[1024] = {0};

while((r=read(fd,buf,1023)) > 0)

{

buf[r] = 0;

printf(“%s”,buf);

}

pclose(fp);

}

1.3 exec系列函数

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 * const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

作用: 替换当前代码空间中的数据

函数本身不创建新的进程

第一个参数:替换的程序

第二个参数:

命令行

命令行格式:命令名 选项参数 NULL

命令行结尾必须是空字符串

execl execlp的区别:

execl   只在当前路径搜索

execlp 可以使用系统搜索路径(which能找到)

如果都找不到,可以使用绝对路径

命令锁指向的程序  命令本身 参数列表

如果返回-1  失败

#include

intmain()

{

intr = execl(“/bin/ls”,”ls”,”-l”,0);// 只能调用当前路径

//  int r = execlp(“ls”,”ls”,”-l”,0);// 能调用系统路径

printf(“调用结束%d\n”,r);

return0;

}

1.4 fork

pid_t fork()

1 创建进程

2 新进程的代码是什么:克隆父进程的挨骂

而且克隆来执行的位置

3 在子进程中不调用fork 所有返回值=0

4 父子进程同时执行

#include

#include

intmain()

{

printf(“创建进程前\n”);

intpid = fork();//父进程执行的

while(1)

{

if(pid == 0)

{

printf(“子进程 %d\n”,getpid());

}

else

{

printf(“父进程 %d\n”,getpid());

}

}

}

3 应用进程

使用fork实现多任务(unix本身是不支持线程的)

1 进程

2 线程

3 信号

4 异步

5 进程池与线程池

4 理解进程

1父子进程的关系

独立的两个进程

互为父子关系

使用pstree看到

├─gnome-terminal─┬─bash───a.out───a.out //父子关系

│                ├─bash───pstree

│                ├─gnome-pty-helpe

│                └─2*[{gnome-terminal}]

2 问题:

1如果父进程先结束 子进程在么办

子进程将变成孤儿进程,直接依托根进程(init)

孤儿 进程是没有危害的

init─┬─NetworkManager─┬─dhclient

│                └─{NetworkManager}

├─a.out

2 如果子进程先结束 父进程怎么办

子进程先结束,子进程会成为僵尸进程。

僵尸进程的特点: 不占用内存 cpu,但在进程任务管理树上占用一个节点(宝贵)

实际上僵尸进程会造成进程名额的资源浪费。一定要处理僵尸进程

├─gnome-terminal─┬─bash───pstree

│                ├─bash───a.out───a.out

3 僵尸进程使用wait回收(阻塞函数)

#include

#include

pid_t wait(int *status);

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

wait 阻塞直到任意进程结束,status来接收进程返回值,返回值表示返回的进程号

waitpid 阻塞直到指定进程结束

WEXITSTATUS(status) 解析返回值  status的 8-15位是进程的返回值

4 父进程怎么知道子进程退出?

子进程结束时,通常会向父进程发送一个SIGCHLD信号

5父进程处理子进程的信号

#include

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

signal 的功能 :

向系统注册,只要接收到信号signal,系统停止进程,执行handler函数,

当函数执行完毕,继续原来的进程 (软中断)

5.1实现处理函数

5.2 使用signal绑定信号与函数

只有当子进程退出时才用wait,因为wait是一个阻塞函数。所以wait和signal一起用。

#include

#include

#include

#include

#include

voiddeal(ints)

{

printf(“回收中\n”);

sleep(5);

intstatus;

wait(&status);

printf(“回收完毕%d\n”,WEXITSTATUS(status));

}

intmain()

{

printf(“创建进程前\n”);

intpid = fork();//父进程执行的

if(pid == 0)

{

printf(“子进程 %d\n”,getpid());

sleep(5);

return88;

}

else

{

printf(“父进程 %d\n”,getpid());

signal(SIGCHLD,deal);

while(1)

{

sleep(1);

printf(“parent\n”);

}

return0;

}

}

zhao@ubuntu:~/unix/5$ ./a.out

创建进程前

父进程 2324

子进程 2325

parent

parent

parent

parent

回收中

回收完毕88

parent

parent

parent

^C

6 父子进程的内存空间

6.1全局变量 局部变量 堆变量 都会被子进程拷贝,但与原先的独立。

注意 堆内存被复制了,需要分别在各个进程中手动释放。

子进程克隆了父进程的全局区和局部区内存,但内存区域指向不同的物理空间。

尽管克隆但内存独立,不能相互访问。

进程间通信(IPC)是大问题。

6.2 内存映射与子进程:

内存映射的属性,决定子进程和父进程是否映射在同一物理空间。

MAP_SHARED: 映射到同一物理空间。 (改一个进程中的,其他进程的也变化)

MAP_PRIVATE:映射到不同的物理空间。

#include

#include

#include

intmain()

{

int*a = mmap(0,4,PROT_READ|PROT_WRITE,

MAP_ANONYMOUS|MAP_SHARED,0,0);

*a = 20;

intpid = fork();//父进程执行的

if(pid == 0)

{

*a= 90;

printf(“parent :a=%d\n”,*a);//90

}

else

{

sleep(3);

printf(“child :a=%d\n”,*a);//90

}

}

因为使用的是MAP_SHARED ,所以射到了同一物理空间, 改动会影响其它的.

若使用MAP_PRIVATE 就可以映射到不同物理空间.

6.3 文件描述符的拷贝

每个进程都维护一个文件描述符列表。

父子进程间,拷贝了文件描述符, 相同文件描述符指向的是同一个文件内核对象。

1 各个进程的文件描述符都需要close。

2 对文件的读写会改变文件对象的读写指针位置,对所有进程都有影响。

#include

#include

#include

#include

intmain()

{

intfd = open(“test.txt”,O_RDWR);

intpid = fork();//父进程执行的

if(pid == 0)

{

printf(“parent:\n”);

charbuf[1024] ={0};

lseek(fd,0,SEEK_SET);

read(fd,buf,1023);

printf(“%s\n”,buf);

close(fd);

}

else

{

printf(“child:\n”);

charbuf[1024] ={0};

lseek(fd,0,SEEK_SET);

read(fd,buf,1023);

printf(“%s\n”,buf);

close(fd);

}

}