我將通過代碼的方式說明一些進程的基本操作
獲取當前進程的ID 和父進程ID
#include<stdio.h>
#include<unistd.h> //包含我們進程相關的系統調用
int main(int argc,char* *argv){
pid_t my_pid,parent_pid; //pid_t是我們進程號的宏定義
my_pid = getpid();//getpid()獲取當前進程的ID
parent_pid = getppid();//獲取父進程ID
printf("my_pid = %d\n",my_pid);
printf("parent_pid = %d\n",parent_pid);
return 0;
}
執行結果
我們還可以通過查看父進程看下我們的程序是哪一個進程創建的。調用命令ps -ef | grep 20524得到結果-bash啦
進程的創建
linux系統中創建進程採用fork系統調用,fork出來的進程除了pid其他的和父進程一樣。爲了區分父進程和子進程我們需要判斷fork的返回值。失敗返回-1,如果是父進程則返回子進程的ID,子進程就會返回0。如果沒聽明白的話。我們試想一下,父進程P與子進程C的程序是一樣的,我們創建C肯定是爲了執行不一樣的任務。一樣的代碼如何執行不同的任務?所以就需要fork的返回值()來做分支判斷。如果是0說明當前運行的是子進程C,就調用fork==0的分支,否則當前就是父進程在運行,就執行其他的。下面看代碼:
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<errno.h>
int main(int argc,char* * argv){
pid_t child;
printf("父進程ID = %d\n",getpid());
child = fork();
if (child == -1){
printf("fork error\n");
}else if(child == 0){
printf("子進程ID = %d\n",getpid());
sleep(5);
exit(0);
}else{
sleep(10);
printf("parent again\n");
exit(0);
}
}
執行結果
我們剛纔說到了根據fork()返回值判斷是子進程還是父進程,那麼我們創建了子進程過後。如何執行不同的程序呢?可以使用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[]);
- int execve(const char *path, char *const argv[], char *const envp[]);
調用exec時進程被新的執行程序替代,然後從main開始執行,下面我們看一個例子:
創建test.c testexec.c文件,通過testexec.c中創建的子進程調用test的可執行文件,代碼如下:
#test.c文件
#include<stdio.h>
#include<string.h>
main(){
int a,b,c;
int *p = NULL;
a = 1;
b = 2;
c = a+++b;
p = &c;
printf("*p = %d\n",*p);
}
#testexec.c文件
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<errno.h>
int main(int argc,char* * argv){
pid_t child;
printf("父進程ID = %d\n",getpid());
child = fork();
if (child == -1){
printf("fork error\n");
}else if(child == 0){
printf("子進程ID = %d\n",getpid());
sleep(2);
execl("/root/gdbtest/exec/test",NULL);//執行同目錄下的test文件
printf("執行execl失敗\n");//如果調用失敗輸出
exit(0);
}else{
printf("parent again\n");
exit(0);
}
}
從下圖的執行結果我們可以看到成功的調用的test文件並且輸出了3
進程間的通信
我們都知道進程間的通信包括管道、信號量、共享內存、消息隊列、Socket的方式,但是我們可能都停留在理論的層面,所以用消息隊列來做個實踐。我們可以使用ipcs查看系統IPC
可以通過msgget()建立或者訪問消息隊列,通過msgsnd和msgrcv發送讀取消息
通過一個例子說明,包括消息發送端和消息接受端
#發送端的程序,模擬消息隊列的發送操作
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
struct my_msg{
long int my_msg_type;
char text[BUFSIZ];
}
main(void){
int running = 1;
int msgid;
struct my_msg msgbuf;
msgid = msgget((key_t)1234,0666|IPC_CREAT);
if (msgid == -1){
printf("message error\n");
exit(1);
}
while(running){
printf("enter some test:\n");
fgets(msgbuf.text,BUFSIZ,stdin);
msgbuf.my_msg_type = 1;
if(msgsnd(msgid,(void*)&msgbuf,BUFSIZ,0) == -1){
printf("msg send error\n");
exit(1);
}
if(strncmp(msgbuf.text,"end",3) == 0){
running = 0;
}
}
exit(0);
}
#模擬消息的接受
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
struct my_msg{
long int my_msg_type;
char text[BUFSIZ];
}
main(void){
int running = 1;
int msgid;
struct my_msg msgbuf;
long int msg_to_receive = 0;
msgid = msgget((key_t)1234,0666|IPC_CREAT);
if (msgid == -1){
printf("message error\n");
printf("message error\n");
exit(1);
}
while(running){
if(msgrcv(msgid,(void*)&msgbuf,BUFSIZ,msg_to_receive,0) == -1){
printf("msg receive error\n");
exit(1);
}
printf("%s\n",msgbuf.text);
if(strncmp(msgbuf.text,"end",3) == 0){
running = 0;
}
}
if(msgctl(msgid,IPC_RMID,0) == -1)
{
printf("msg del error \n");
exit(1);
}
exit(0);
}
最後我們運行程序看看效果
輸入前:
輸入後