进程:
Linux系统至少有一个进程,一个程序可以对应多个进程,一个进程只能对应一个程序。
由于历史原因,大多数UNIX系统的main()函数定义为:
int main(int argc, char *argv[ ], char *envp[ ]);
argc表示参数argv有多少个字符串,argv表示一个不定长的字符串数组,envp以name=value的形式存放了一组进程中会用到的环境变量。
环境变量:
读取环境变量:getenv();
设定环境变量:putenv();
char *getenv(const char *name); //name是要获取的环境变量名字
POSIX定义的环境变量及其含义:
HOME:起始目录
LANG:本地名(本地语言类型)
LC_ALL:本地名
LC_COLLATE:本地排序名等等
在程序中获得环境变量:gcc path.c -o path
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *env_path = "PATH";
char *env_value = NULL; //环境变量值
env_value = getenv(env_path); //使用系统函数获取指定环境变量
if(NULL==env_value) //检查是否获取到变量的值
printf("Not found!\n");
printf("Get Env PATH:\n%s\n",env_value); //输出PATH环境变量的值
return 0;
}
【运行结果】
lwb@ubuntu:~/mmanager$ ./path
Get Env PATH:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/lwb/SYSTEM/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
进程建立:
一个进程的基本属性主要包括:进程号,父进程号,进程组号,真实用户号,真实组号,有效用户号,有效组号。
fork()函数创建进程成功后,在此之后运行的已经是两个进程。子进程和父进程都可以得到fork()函数的返回值,对于子进程,返回值是0,对于父进程,返回值是子进程的进程号。如果创建子进程失败了,fork()函数会给父进程返回-1;
fork()函数创建子进程后,会复制父进程的数据段,代码段和堆栈空间等到子进程的空间。子进程可以共享父进程中打开的文件,即父进程已经打开的文件,在子进程中可以直接操作。
【fork()函数创建子进程】
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid=fork(); //创建进程
if(pid==-1) //创建进程失败
{
printf("creat child process is faild\n");
return 0;
}
else if (pid==0) //子进程
{
printf("child process is sucess--id:%d\n",pid);
}
else //父进程
{
printf("Parent process! child process id:%d\n",pid);
}
return 0;
}
【运行结果】
lwb@ubuntu:~/mmanager$ gcc process.c -o process
lwb@ubuntu:~/mmanager$ ./process
Parent process! child process id:4524
lwb@ubuntu:~/mmanager$ child process is sucess–id:0
所有的进程都是从init进程创建来的,当进程结束的时候它的父进程会收回子进程的资源。但是父进程和子进程是无序进行的,此时需要调用waitpid()函数,等待一个进程结束。
pid_t waitpid(pid_t pid, int *status, int options);
【waitpid()函数等待子进程】
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid, pid_wait;
int status;
pid=fork(); //创建子进程
if(pid==-1) //检查是否创建成功
{
printf("error to create new process!\n");
return 0;
}
else if(pid==0) //子进程
{
printf("child process!\n");
}
else //父进程
{
printf("parent process! chile process ID:%d\n",pid);
pid_wait=waitpid(pid, &status, 0); //等待指定号的子进程
printf("child process %d returned!\n",pid_wait);
}
return 0;
}
【运行结果】
lwb@ubuntu:~/mmanager$ gcc wait.c -o wait
lwb@ubuntu:~/mmanager$ ./wait
parent process! chile process ID:5122
child process!
child process 5122 returned!
退出进程函数:exit() _exit() atexit() on_exit()。eixt()函数的作用是退出当前进程,并且尽可能释放当前进程占用的资源。
int atexit(void (*function)(void));
int on_exit(void (*function)(int , void *), void *arg);
void exit(int status);
void _exit(int status);
【atexit()函数退出回调函数例程】
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void bye(void)
{
printf("thid was all, folks\n");
}
void bye1(void)
{
printf("this should called first!\n");
}
int main()
{
long a;
int i;
i=atexit(bye);
if(i!=0)
{
fprintf(stderr,"cannot set exit function bye\n");
return EXIT_FAILURE;
}
i=atexit(bye1);
if(i!=0)
{
fprintf(stderr,"cannot set exit function bye1\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
【运行结果】
lwb@ubuntu:~/mmanager$ gcc exit.c -o exit
lwb@ubuntu:~/mmanager$ ./exit
this should called first!
thid was all, folks
不同的进程间需要传递数据时,会采取进程间通信,常见的包括管道,FIFO,消息队列,信号量,共享存储以及socket等。
管道通信:
管道通信有两个限制,一是管道通信是半双工的,二是管道只能在有共同父进程的进程间使用。通常,管道由一个进程创建,之后进程调用fork()创建新的进程,父进程和子进程之间就可以使用管道通信了。
int pipe(int filedes[2]); //参数fieldes返回两个文件描述符,filedes[0]为读打开,filedes[1]为写打开。当创建成功时pipe()返回0,如果创建失败则会返回-1。
【pipe()函数管道通信例程】
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd[2];
pid_t pid;
char buf[64]="I am parent process!\n"; //父进程要写入管道的信息
char line[64];
if(pipe(fd)!=0) //创建管道并检查结果
{
fprintf(stderr,"fail to creat pipe!\n");
return 0;
}
pid=fork(); //创建进程
if(pid<0)
{
fprintf(stderr,"fail to creat process!\n");
return 0;
}
else if(pid==0) //父进程
{
close(fd[0]); //关闭读管道,使得父进程只能向管道写入数据
write(fd[1], buf, strlen(buf)); //写数据到管道
close(fd[1]); //关闭写管道
}
else //子进程
{
close(fd[1]); //关闭写管道,使得子进程只能从管道读取数据
read(fd[0], line, 64); //从管道读取数据
printf("DATA from parent: %s", line);
close(fd[0]); //关闭读管道
}
return 0;
}
【运行结果】
lwb@ubuntu:~/mmanager$ vi pipe.c
lwb@ubuntu:~/mmanager$ gcc pipe.c -o pipe
lwb@ubuntu:~/mmanager$ ./pipe
DATA from parent: I am parent process! //子进程打印出了父进程通过管道发送来的字符串
共享内存:
共享内存是在内存中开辟一段空间,供不同的进程访问,即多个不同进程(非父子进程)间共享数据,比管道传送更大量的数据。
共享内存在使用之前需要先创建,之后获得共享内存的入口地址就可以对共享内存进行操作了。如果不需要使用共享内存的时候,还可以在程序中分离共享内存。
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
shmget()函数用来创建共享内存。
shmat()函数是获得一个共享内存ID对应的内存起始地址。
shmdt()函数从程序中分离一块共享内存。
【shm_write.c写入共享内存操作】
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main()
{
int shmid; //定义共享内存ID
char *ptr;
char *shm_str = "string in a share memory";
shmid = shmget(0x90,1024,SHM_W|SHM_R|IPC_CREAT|IPC_EXCL); //创建共享内存
if(shmid==-1)
perror("create share memory");
ptr = (char *)shmat(shmid, 0, 0); //通过共享内存ID获得共享内存地址
if((void *)-1==ptr)
perror("get share memory");
strcpy(ptr, shm_str); //把字符串写入共享内存
shmdt(ptr);
return 0;
}
【shm_read.c读取共享内存操作】
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main()
{
int shmid; //定义共享内存ID
char *ptr;
shmid = shmget(0x90, 1024, SHM_W|SHM_R|IPC_EXCL); //根据key获得共享内存ID
if(shmid==-1)
perror("creare share memory");
ptr = shmat(shmid, 0, 0); //通过共享内存ID获得共享内存地址
if((void *)-1==ptr)
perror("get share memory");
printf("string in share memory: %s\n", ptr); //打印共享内存中的内容
shmdt(ptr);
return 0;
}
【运行结果】
lwb@ubuntu:~/sharemem$ gcc shm_write.c -o w
lwb@ubuntu:~/sharemem$ gcc shm_read.c -o r
lwb@ubuntu:~/sharemem$ ./w
lwb@ubuntu:~/sharemem$ ./r
string in share memory: string in a share memory
lwb@ubuntu:~/sharemem$ ./w
create share memory: File exists
get share memory: Invalid argument
Segmentation fault (core dumped) //出错原因在于第二次写入时,已经有相同的key值的共享内存了,实例中退出的时候没有删除共享。
lwb@ubuntu:~/sharemem$ ipcs //查看目前共享资源情况
lwb@ubuntu:~/sharemem$ ipcrm -m 27 //成功删除共享内存
【进程编程实例】
实例中,父进程和子进程之间通过管道进行传输数据,父进程向子进程发送字符串exit表示子进程退出,并且等待子进程返回,子进程查询管道,当从管道读出字符串exit的时候结束。
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid, pid_wait;
int fd[2];
char buff[64], *cmd="exit";
if(pipe(fd)) //创建管道
{
perror("create pipe fail!");
return 0;
}
pid = fork();
if(pid==-1)
{
perror("create process fail!");
return 0;
}
else if(pid==0) //子进程
{
close(fd[1]); //关闭写操作
printf("wait command from parent!\n");
while(1)
{
read(fd[0], buff, 64);
if(strcmp(buff,cmd)==0)
{
printf("recv command ok!\n");
close(fd[0]);
exit(0);
}
}
}
else //父进程
{
printf("Parent process! child process id: %d\n", pid);
close(fd[0]); //关闭读操作
sleep(2);
printf("Send command to child process\n");
write(fd[1], cmd, strlen(cmd)+1); //写入命令
close(fd[1]);
}
return 0;
}
【运行结果】
lwb@ubuntu:~/process$ gcc process.c -o process
lwb@ubuntu:~/process$ ./process
Parent process! child process id: 5762
wait command from parent!
Send command to child process
lwb@ubuntu:~/process$ recv command ok!
摘自:弓雷 ARM嵌入式Linux系统开发详解