系統程式設計函式之程序及程序通訊
阿新 • • 發佈:2019-02-03
1、產生一個子程序
NAME
fork - create a child process
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
RETURN VALUE
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.
使用:
pid_t pid = -1;
//產生一個子程序
pid = fork();
if (pid > 0)
//parent
{
num = 2;
strcpy(caMsg, "this is parent");
}
else if (0 == pid)
//child
{
num = 6;
strcpy(caMsg, "this is child");
}
else if (-1 == pid)
//error
{
perror("fork");
exit(EXIT_FAILURE);
}
2、等待程序死亡
NAME
wait, waitpid, waitid - wait for process to change state
SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
RETURN VALUE
wait(): on success, returns the process ID of the terminated child; on error, -1 is returned.
waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more
child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
waitid(): returns 0 on success or if WNOHANG was specified and no child(ren) specified by id has yet changed state; on error, -1 is
returned.
Each of these calls sets errno to an appropriate value in the case of an error.
使用:wait(NULL);(為殭屍程序收屍)
3、呼叫可執行檔案
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, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
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[]);
RETURN VALUE
The exec() functions return only if an error has occurred. The return value is -1, and errno is set to indicate the error.
//引數一:執行檔案的路徑
//引數二:要執行的檔名
//後續引數為檔案執行時的引數
//最後一個引數設定為NULL
execl("/home/sq/tmp/test", "test", NULL);
execl("/usr/bin/gedit" , "gedit", "./execl.c", NULL);
execlp("test", "./test", NULL);
(省略了路徑,如何新增預設路徑)
/etc
sudo vim profile
export PATH=/home/shenglin/today:$PATH
scourse profile
只對當前終端有效
重啟後全有效
char *arg[] = {"gedit", "./execlp.c", NULL};
execv("/usr/bin/gedit", arg);
只有ececve是正真意義上的系統呼叫,其他都是在此基礎上經過包裝的庫函式。
exec函式族的作用是根據指定的檔名找到可執行檔案,並用它來調取程序中的內容,也就是 呼叫程序內部執行一個可執行檔案,可以是二進位制檔案,也可以是Linux下的指令碼檔案
執行成功後不會返回,因為呼叫程序的實體,包括程式碼段,資料段,堆疊等都已經被新的內容取代,只留下程序ID等一些表面上的資訊仍然保持原樣,金蟬脫殼,看上去還是舊軀殼,卻注入了新靈魂,只有失敗才會返回-1;
Linux下如何執行新程式?
每當有程序認為自己不能為系統和使用者做出貢獻了,他就發揮最後的餘熱,呼叫ecec,讓自己重生;or
若一個程序想執行另一個程式,可以fork一個新程序,呼叫exec,看起來像是執行應用程式產生了新程序(非常普遍)
Linux專門做了優化,fork會將所有內容拷貝到子程序,而fork完後呼叫exec,這些拷貝的東西又會被抹掉,
於是人們設計了寫時拷貝(copy-on-write)技術,使得fork結束後不立刻複製父程序的內容,到了真正實用的時候才複製,這樣如果下一條語句是exec,就不會做無用功了,提高了效率。
一定要加錯誤判斷,因為許可權問題,總會出差錯
1、無名管道 pipe (相當於定義了一個數據結構用於通訊,程式結束消失)
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
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
使用:
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'};
//防止父程序意外將資料讀取
//可以將讀端關閉
close(pipefd[0]);
while (1)
{
printf("父程序輸入:\n");
memset(caMsg, '\0', sizeof(caMsg));
//從標準輸入獲得資料
read(STDIN_FILENO, caMsg, 64);
//將資料寫入管道
write(pipefd[1], caMsg, strlen(caMsg));
}
}
else if (0 == pid) //child
{
char caMsg[64] = {'\0'};
//防止子程序意外往管道中寫資料
//可以將寫端關閉
close(pipefd[1]);
while (1)
{
memset(caMsg, '\0', 64);
//從管道中讀取資料
//若管道中沒有資料,則阻塞等待資料
read(pipefd[0], caMsg, 64);
//將資料顯示
printf("子程序顯示資料:\n");
write(STDOUT_FILENO, caMsg, strlen(caMsg));
}
}
else if (-1 == pid) //error
{
perror("fork");
return -1;
}
pipefd[1] 寫入埠,pipefd[0] 讀取埠,程式結束管道消失
無名管道資料結構,資料讀取後就清除,引數存放介面描述符,管道獨立於程序,屬於第三方,子程序僅拷貝描述符,只能用於具有親緣關係的程序間通訊,建立無名管道的程序結束後,無名管道也被釋放
2、有名管道;mkfifo (建立了管道檔案用於通訊,永遠存在)
NAME
mkfifo - make FIFOs (named pipes)
SYNOPSIS
mkfifo [OPTION]... NAME...
使用:
int ret = mkfifo("myfifo", 0664);
if (-1 == ret)
{
perror("mkfifo");
return -1;
}
讀資料:
int main(int argc, char *argv[])
{
int fd = open("myfifo", O_RDWR);
char caMsg[64] = {'\0'};
int sign = 1;
while (1)
{
memset(caMsg, '\0', sizeof(caMsg));
if (0 == sign)
{
printf("請輸入資料:\n");
read(STDIN_FILENO, caMsg, 64);
write(fd, caMsg, strlen(caMsg));
sign = 1;
}
else if (1 == sign)
{
read(fd, caMsg, 64);
printf("獲得資料:\n");
write(STDOUT_FILENO, caMsg, strlen(caMsg));
sign = 0;
}
sleep(1);
}
printf("Hello World\n");
return 0;
}
寫資料:
int main(int argc, char *argv[])
{
int fd = open("myfifo", O_RDWR);
char caMsg[64] = {'\0'};
int sign = 0;
while (1)
{
memset(caMsg, '\0', sizeof(caMsg));
if (0 == sign)
{
printf("請輸入資料:\n");
read(STDIN_FILENO, caMsg, 64);
write(fd, caMsg, strlen(caMsg));
sign = 1;
}
else if (1 == sign)
{
read(fd, caMsg, 64);
printf("獲得資料:\n");
write(STDOUT_FILENO, caMsg, strlen(caMsg));
sign = 0;
}
sleep(1);
}
printf("Hello World\n");
return 0;
}
p開頭的是管道檔案,若寫開啟一個有名管道,則會阻塞,直到有程序讀開啟該有名管道,同理讀開啟一樣
若寫段關閉,read返回值為0,若在執行過程中,讀段關閉,則系統會發送訊號幹掉本程序
3、共享記憶體 (向計算機申請一塊記憶體用於通訊,電腦關機後消失)
(1)建立:shmget
SHMGET(2) Linux Programmer's Manual SHMGET(2)
NAME
shmget - allocates a System V shared memory segment
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
key 十六進位制標識,共享記憶體編號,編號可以相同,size:共享記憶體大小,shmflg:下列
int shmget(key_t key, size_t size, int shmflg);
RETURN VALUE 返回共享編號:共享編號唯一 若存在,只返回共享編號
On success, a valid shared memory identifier is returned. On error, -1 is returned, and errno is set to indicate the error.
IPC_CREAT Create a new segment. If this flag is not used, then shmget() will find the segment associated with key and check to
see if the user has permission to access the segment.
IPC_EXCL This flag is used with IPC_CREAT to ensure that this call creates the segment. If the segment already exists, the call
fails.
SHM_HUGETLB (since Linux 2.6)
Allocate the segment using "huge pages." See the Linux kernel source file Documentation/vm/hugetlbpage.txt for further
information.
SHM_HUGE_2MB, SHM_HUGE_1GB (since Linux 3.8)
Used in conjunction with SHM_HUGETLB to select alternative hugetlb page sizes (respectively, 2 MB and 1 GB) on systems
that support multiple hugetlb page sizes.
More generally, the desired huge page size can be configured by encoding the base-2 logarithm of the desired page size
in the six bits at the offset SHM_HUGE_SHIFT. Thus, the above two constants are defined as:
#define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT)
#define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT)
For some additional details, see the discussion of the similarly named constants in mmap(2).
SHM_NORESERVE (since Linux 2.6.15)
This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. Do not reserve swap space for this segment.
在記憶體中申請空間,返回地址,讀完後資料依然存在,程式結束後依然存在(malloc 消失),在電腦關機時消失
(2)建立聯絡:shmat
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg); (0讀寫,SHM_RDONLY )
int shmdt(const void *shmaddr);
RETURN VALUE
On success, shmat() returns the address of the attached shared memory segment; on error, (void *) -1 is returned, and errno is set
to indicate the cause of the error.
On success, shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the error.
使用:
int main(int argc, char *argv[])
{
int shmid = -1;
shmid = shmget(0x1024, 4096, IPC_CREAT|0664);
if (-1 == shmid)
{
perror("shmget");
return -1;
}
void *addr = NULL;
//引數二:NULL讓系統自動將共享記憶體關聯到程序合適位置
//引數三:0表示讀寫
//SHM_RDONLY:表示只讀
//沒有隻寫
//成功:返回值為共享記憶體的地址
//失敗:返回值為(void *)-1
addr = shmat(shmid, NULL, 0);
if ((void *)-1 == addr)
{
perror("shmat");
return -1;
}
char *pMsg = "歡迎來到上海";
//將資料拷貝到共享記憶體中
//strcpy((char *)addr, pMsg);
//sprintf((char*)addr, "%s", pMsg);
//memcpy(addr, pMsg, strlen(pMsg));
char *p = (char *)addr;
while ('\0' != *pMsg)
{
*p = *pMsg;
p++;
pMsg++;
}
*p = '\0';
//將資料從共享記憶體中拷貝出來
char caMsg[64] = {'\0'};
// strcpy(caMsg, (char*)addr);
// sscanf((char*)addr, "%s", caMsg);
memcpy(caMsg, addr, 64);
printf("msg:%s\n", caMsg);
//取消關聯
shmdt(addr);
return 0;
}
4、檔案對映:mmap,munmap
NAME
mmap, munmap - map or unmap files or devices into memory
SYNOPSIS
#include <sys/mman.h>
(地址NULL, 大小, 作用讀還是寫,是本程序自己用還是一起用,檔案描述符,)
void *mmap(void *addr, size_t length, int prot,
int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
MAP_SHARED (flags)
Share this mapping. Updates to the mapping are visible to other processes that map this file, and are carried through to
the underlying file. (To precisely control when updates are carried through to the underlying file requires the use of
msync(2).)
MAP_PRIVATE
Create a private copy-on-write mapping. Updates to the mapping are not visible to other processes mapping the same file,
and are not carried through to the underlying file. It is unspecified whether changes made to the file after the mmap()
call are visible in the mapped region.
RETURN VALUE
On success, mmap() returns a pointer to the mapped area. On error, the value MAP_FAILED (that is, (void *) -1) is returned, and
errno is set to indicate the cause of the error.
On success, munmap() returns 0. On failure, it returns -1, and errno is set to indicate the cause of the error (probably to EIN‐
VAL).
使用:
int main(int argc, char *argv[])
{
int fd = -1;
//以讀寫的方式開啟檔案
fd = open(argv[1], O_RDWR);
if (-1 == fd)
{
perror("open");
return -1;
}
void *addr = NULL;
//1:NULL表示讓系統自動對映到記憶體的合適位置
//2:儲存空間的大小
addr = mmap(NULL, 1024, PROT_READ|PROT_WRITE
, MAP_SHARED, fd, 0);
//MAP_FAILED --> (void *)-1
if (MAP_FAILED == addr)
{
perror("mmap");
return -1;
}
char *pData = "北京歡迎你";
sprintf((char *)addr, "%s", pData);
close(fd);
munmap(addr, 1024);
return 0;
}
一般不用做通訊,用在對裝置的操作,U盤,攝像頭,把攝像頭當做檔案對映到記憶體
將檔案的一塊內容放入記憶體,對記憶體的操作,會自動更新到檔案中